Search This Blog

Showing posts with label JDBC. Show all posts
Showing posts with label JDBC. Show all posts

Thursday, 4 September 2014

Variable in list with Postgres JDBC and Greenplum

I previously blogged on how to create a variable JDBC IN list with Oracle. Here is how you would do it with Pivotal Greenplum. Much easier , without having to write a function. In the Greenplum demo below we use the any function combined with string_to_array

http://theblasfrompas.blogspot.com.au/2008/02/variable-in-list-with-oracle-jdbc-and.html

Code as follows
  
import java.sql.*;
import java.sql.DriverManager;

/**
 * Created by papicella on 4/09/2014.
 */
public class VariableInListGreenplum
{

    public VariableInListGreenplum()
    {
    }

    private Connection getConnection() throws SQLException, ClassNotFoundException
    {
        Class.forName("org.postgresql.Driver");
        Connection conn = null;
        conn = DriverManager.getConnection(
                "jdbc:postgresql://127.0.0.1:5432/apples","pas", "pas");

        return conn;
    }

    public void run() throws SQLException
    {
        Connection conn = null;
        PreparedStatement stmt = null;
        ResultSet rset = null;
        String queryInList =
                        "SELECT DEPTNO, " +
                        "       DNAME, " +
                        "       LOC " +
                        "FROM   scott.DEPT " +
                        "WHERE DEPTNO = any(string_to_array(?,', ')) ";

        try
        {
            conn = getConnection();
            stmt = conn.prepareStatement(queryInList);
            stmt.setString(1, "10, 20, 30");
            rset = stmt.executeQuery();

            while (rset.next())
            {
                System.out.println("Dept [" + rset.getInt(1) + ", " +
                                              rset.getString(2) + "]");
            }
        }
        catch (Exception e)
        {
            System.out.println("Exception occurred");
            e.printStackTrace();
        }
        finally
        {
            if (conn != null)
            {
                conn.close();
            }

            if (stmt != null)
            {
                stmt.close();
            }

            if (rset != null)
            {
                rset.close();
            }
        }
    }

    public static void main(String[] args) throws Exception
    {
        VariableInListGreenplum test = new VariableInListGreenplum();
        test.run();
    }
}

Thursday, 5 December 2013

HikariCP (Connection Pool) with Pivotal GemFireXD

I decided to try out the HikariCP as per the link below it says it's the fastest Connection Pool and the most lightweight. It probably is so I thought I would set it up for GemFireXD.

Quote: There is nothing faster.1 There is nothing more correct. HikariCP is a "zero-overhead" production-quality connection pool. Coming in at roughly 50Kb, the library is extremely light.

https://github.com/brettwooldridge/HikariCP

Example Below.

1. Create a Pool class as follows
  
package pivotal.au.gemfirexd.demos.connectionpool;

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

import java.sql.Connection;
import java.sql.SQLException;

public class HikariGFXDPool
{

    private static HikariGFXDPool instance = null;
    private HikariDataSource ds = null;

    static
    {
        try
        {
            instance = new HikariGFXDPool();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e.getMessage(), e);
        }

    }

    private HikariGFXDPool()
    {
        HikariConfig config = new HikariConfig();
        config.setMaximumPoolSize(10);
        config.setMinimumPoolSize(2);

        config.setDataSourceClassName("com.vmware.sqlfire.internal.jdbc.ClientDataSource");

        config.addDataSourceProperty("portNumber", 1527);
        config.addDataSourceProperty("serverName", "192.168.1.6");
        config.addDataSourceProperty("user", "app");
        config.addDataSourceProperty("password", "app");

        ds = new HikariDataSource(config);
    }

    public static HikariGFXDPool getInstance ()
    {
        return instance;
    }

    public Connection getConnection()  throws SQLException
    {
        return ds.getConnection();
    }

}
2. Create a Test Class as follows
  
package pivotal.au.gemfirexd.demos.connectionpool;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestPool
{
    private Logger logger = Logger.getLogger(this.getClass().getSimpleName());

    public void run () throws SQLException
    {
        Statement   stmt = null;
        ResultSet   rset = null;
        Connection  conn = null;

        HikariGFXDPool pool = HikariGFXDPool.getInstance();

        try
        {
            conn = pool.getConnection();
            stmt = conn.createStatement();
            rset = stmt.executeQuery("select * from dept order by 1");
            while (rset.next())
            {
                System.out.println(String.format("Dept[%s, %s, %s]",
                                        rset.getInt(1),
                                        rset.getString(2),
                                        rset.getString(3)));
            }
        }
        catch (SQLException se)
        {
            logger.log(Level.SEVERE, se.getMessage());
        }
        finally
        {
            if (stmt != null)
            {
                stmt.close();
            }
            
            if (rset != null)
            {
                rset.close();
            }
            
            if (conn != null)
            {
                conn.close();
            }

        }

    }

    public static void main(String[] args) throws SQLException
    {
        TestPool test = new TestPool();
        test.run();
    }
} 

3. Run Test class and verify output as follows


SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Dept[10, ACCOUNTING, NEW YORK]
Dept[20, RESEARCH, DALLAS]
Dept[30, SALES, CHICAGO]
Dept[40, OPERATIONS, BRISBANE]
Dept[50, MARKETING, ADELAIDE]
Dept[60, DEV, PERTH]
Dept[70, SUPPORT, SYDNEY]

Thursday, 19 November 2009

Accessing the Oracle Universal Connection Pool MBean using JConsole

Now I am just tranistioning to the Oracle Universal Connection Pool I found that it has a useful MBean known as "UniversalConnectionPoolMBean" which you can view/manipulate with "jconsole" using JDK 1.6. Here is a screen shot of jconsole attached to the JVM which is has a Universal Connection Pool running within it. Once attached to the JVM access it as follows.

1. Click on the MBean tab
2. Click the + symbol for "oracle.ucp.admin.UniversalConnectionPoolMBean"
3. Click on the "Attributes" or "Methods" node and you can view/update the pool using that MBean

The javadoc for the MBean this can be found here.

http://download.oracle.com/docs/cd/B28359_01/java.111/e11990/oracle/ucp/admin/UniversalConnectionPoolManagerMBean.html

In the example below I was using Oracle's UCP within a coherence cache server for write behind to an Oracle database.

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

Wednesday, 6 May 2009

Oracle Weblogic 10.3 Using SSL with the Oracle JDBC THIN driver

I recently created this how to with product management showing how to configure a data source in Oracle WebLogic 10.3 using SSL with the JDBC THIN driver.

http://www.oracle.com/technology/products/weblogic/howto/jdbcssl/index.html

Monday, 2 March 2009

Java Stored Procedure remotely connecting to another database

I very rarely use JavaSP but I had a request to connect to a remote database from a JavaSP. My first thought was to use a database link but the request required a direct connection to the remote database using JDBC/THIN driver. here is how I got it working.

1. Create a class as follows which contains one method to connect to a remote database.


import java.sql.Connection;

import java.sql.SQLException;

import oracle.jdbc.pool.OracleDataSource;

public class RemoteDBTest
{
public static String remoteConnection()
throws SQLException
{
StringBuffer sb = new StringBuffer();
String userId = "scott";
String password = "tiger";
String url =
"jdbc:oracle:thin:@acampanaro-pc2.au.oracle.com:1521:linux102";
Connection conn = null;

OracleDataSource ods = new OracleDataSource();
ods.setUser(userId);
ods.setPassword(password);
ods.setURL(url);
conn = ods.getConnection();

sb.append("Auto commit = " + conn.getAutoCommit());
conn.close();

return sb.toString();
}
}


2. Load the class into the database and create a call specification, I used JDeveloper 10.1.3.4 as this makes light work of the task as shown by the output below. The class was loaded as user SCOTT.

Invoking loadjava on connection 'scott-linux102' with arguments:
-order -resolve -thin
Loadjava finished.
Executing SQL Statement:
CREATE OR REPLACE FUNCTION remoteConnection RETURN VARCHAR2 AUTHID CURRENT_USER AS LANGUAGE JAVA NAME 'RemoteDBTest.remoteConnection() return java.lang.String';
Success.
Publishing finished.

3. Now I need to grant some privileges to enable me to remotely connect from my database to another database using JAVA. There 2 are as follows, which must be executed as SYS.

SYS@LINUX10G> exec dbms_java.grant_permission( 'SCOTT', 'SYS:java.net.SocketPermission', 'acampanaro-pc2.au.oracle.com', 'resolve');

PL/SQL procedure successfully completed.

SYS@LINUX10G> exec dbms_java.grant_permission( 'SCOTT', 'SYS:java.net.SocketPermission', '10.187.80.13:1521', 'connect,resolve');

PL/SQL procedure successfully completed.

4. Now we can call our JavaSP and verify if we have successfully connected.

SCOTT@LINUX10G> select remoteconnection from dual;

REMOTECONNECTION
------------------------------------------------------------

Auto commit = true

What you will find is if you don't execute the required privileges then you will be told when you try to run the JavaSP that you don't have required permissions to do this. His an example of what error you may receive. Lucky for me oracle gives me the command I need to run which made this easy to setup.

SCOTT@LINUX10G> select remoteconnection from dual;
select remoteconnection from dual
*
ERROR at line 1:
ORA-29532: Java call terminated by uncaught Java exception: java.security.AccessControlException: the Permission
(java.net.SocketPermission 10.187.80.13:1521 connect,resolve) has not been granted to SCOTT. The PL/SQL to grant this
is dbms_java.grant_permission( 'SCOTT', 'SYS:java.net.SocketPermission', '10.187.80.13:1521', 'connect,resolve' )

Tuesday, 24 February 2009

How to Create a JDBC/OCI Connection Pool For Stand Alone OC4J 10.1.3.x

Recently I had to setup JDBC/OCI connection pool in stand alone OC4J 10.1.3.x using an oracle client 10.2.0.3. The problem here is OC4J 10.1.3.x comes with JDBC driver 10.1.0.5 of ojdbc14.jar. Which means you will have issues using the 10.2.0.3 oracle client libraries.

As a result of that you must upgrade the driver to use the client version JDBC driver as well as set various environment variables before starting stand alone OC4J. The steps are as follows.

Note: This is based on OC4J 10.1.3.4 and on a windows environment, using instant client 10.2.0.3

1. Download 10.2.0.3 instant client as follows, in my case installed in directory as follows

D:\jdev\instant-clients\10203

You can download the 10.2.0.3 version from here.

http://www.oracle.com/technology/software/tech/oci/instantclient/

2. Open up command window and set environment variables as follows

set PATH=D:\jdev\instant-clients\10203;%PATH%
set TNS_ADMIN=D:\jdev\instant-clients\10203

Note: A tnsnames.ora exists in the directory D:\jdev\instant-clients\10203 and has the following entry which I will use in stand alone OC4J shortly.

LINUX10G =
(DESCRIPTION =
(ADDRESS = (PROTOCOL = TCP)(HOST = xxxx.au.oracle.com)(PORT = 1522))
(CONNECT_DATA =
(SERVER = DEDICATED)
(SERVICE_NAME = linux10g)
)
)

3. Start stand alone OC4J as follows, ensuring you use the same commend window from step 2 above.

set ORACLE_HOME=D:\jdev\stand-alone-oc4j\10134
set JAVA_HOME=D:\jdev\jdevprod\10134\jdk

D:\jdev\stand-alone-oc4j\10134\bin>oc4j -start
Starting OC4J from D:\jdev\stand-alone-oc4j\10134\j2ee\home ...
09/02/23 13:42:29 Oracle Containers for J2EE 10g (10.1.3.4.0) initialized

4. OC4J 10.1.3.x is using 10.1.0.5 JDBC driver which means it will have issues connecting using 10.2.0.3 instant client so we have to upgrade the JDBC driver to 10.2.0.3 as shown below for the whole container. Follow this metalink note to fix that so it matches the same version of instant client which would mean you use ojdbc14.jar from 10.2.0.3 jar file of ojdbc14.jar.

Note 420303.1 - How to Use The Latest Thin JDBC Driver Across All Applications For a 10.1.3.x OAS Container

You can download the 10.2.0.3 JDBC driver jar file from this link, but it should be available in your oracle client install.

http://www.oracle.com/technology/software/tech/java/sqlj_jdbc/index.html

5. Stop stand alone OC4J to pick up the shared library change.
6. Start stand alone OC4J.
7. Create a data source as follows.


<connection-pool name="ocitest-pool">
<connection-factory factory-class="oracle.jdbc.pool.OracleDataSource"
user="scott" password="tiger" url="jdbc:oracle:oci:@LINUX10G"/>
</connection-pool>


For more information on data sources refer to the following documentation.

Oracle® Containers for J2EE Services Guide
10g (10.1.3.1.0)
Part Number B28958-01
http://download-uk.oracle.com/docs/cd/B31017_01/web.1013/b28958/datasrc.htm#BABIDCBJ

Thursday, 12 February 2009

Determine the Security Providers for the JVM

Note for myself: While working with Oracle's JDBC/THIN driver with SSL this comes in handy

package pas.test;

import java.security.Provider;
import java.security.Security;

public class GetProviders
{
public GetProviders()
{
}

public static void main(String[] args)
{
Provider[] pl = Security.getProviders();
System.out.println("Printing Available Security.getProviders()");
for (int i=0; i < pl.length; i++)
{
System.out.println("\n" + i + ". " + pl[i].toString());
System.out.println("Name: " + pl[i].getName());
System.out.println("Version: " + pl[i].getVersion());
System.out.println("Info: " + pl[i].getInfo());
}
}
}

Output:

Printing Available Security.getProviders()

0. SUN version 1.5
Name: SUN
Version: 1.5
Info: SUN (DSA key/parameter generation; DSA signing; SHA-1, MD5 digests; SecureRandom; X.509 certificates; JKS keystore; PKIX CertPathValidator; PKIX CertPathBuilder; LDAP, Collection CertStores)

1. SunRsaSign version 1.5
Name: SunRsaSign
Version: 1.5
Info: Sun RSA signature provider

2. SunJSSE version 1.5
Name: SunJSSE
Version: 1.5
Info: Sun JSSE provider(PKCS12, SunX509 key/trust factories, SSLv3, TLSv1)

3. SunJCE version 1.5
Name: SunJCE
Version: 1.5
Info: SunJCE Provider (implements RSA, DES, Triple DES, AES, Blowfish, ARCFOUR, RC2, PBE, Diffie-Hellman, HMAC)

4. SunJGSS version 1.0
Name: SunJGSS
Version: 1.0
Info: Sun (Kerberos v5)

5. SunSASL version 1.5
Name: SunSASL
Version: 1.5
Info: Sun SASL provider(implements client mechanisms for: DIGEST-MD5, GSSAPI, EXTERNAL, PLAIN, CRAM-MD5; server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5)

Tuesday, 20 January 2009

JDBC THIN / SSL 11g Runtime Issue - java.security.InvalidAlgorithmParameterException

I have been working on setting up JDBC THIN /SSL 11g (ojdbc6.jar) with JDK 1.6 against an 11g database. Previously I only ever done this with 10g, so here is a common runtime issue I run into while setting this up from the client side.

Runtime excepotion as follows:

java.sql.SQLRecoverableException: Io exception: java.lang.RuntimeException: Unexpected error: java.security.InvalidAlgorithmParame
terException: the trustAnchors parameter must be non-empty

Due to the fact I was using (PKCS12) wallets (ewallet.p12) I needed place Oracle PKI provider in java.security for my JDK 1.6 runtime environment as shown below.

File : D:\jdev\jdk16\jre\lib\security\java.security

security.provider.1=sun.security.provider.Sun
security.provider.2=sun.security.rsa.SunRsaSign
security.provider.3=oracle.security.pki.OraclePKIProvider
security.provider.4=com.sun.net.ssl.internal.ssl.Provider
security.provider.5=com.sun.crypto.provider.SunJCE
security.provider.6=sun.security.jgss.SunProvider
security.provider.7=com.sun.security.sasl.Provider
security.provider.8=org.jcp.xml.dsig.internal.dom.XMLDSigRI
security.provider.9=sun.security.smartcardio.SunPCSC
security.provider.10=sun.security.mscapi.SunMSCAPI

Once that was done the error dissapeared I was able to run my client side JDBC THIN/ SSL demo.

D:\temp\pas-ssl>java -classpath ./classes;.;D:\temp\pas-ssl\lib\ojdbc6.jar;D:\temp\pas-ssl\lib\oraclepki.jar support.au.jdbc.ssl.J
DBCSSLWalletTrustore
Start: Tue Jan 20 09:16:20 EST 2009
Conncted as DATABASE USER SCOTT
Ended: Tue Jan 20 09:16:21 EST 2009

D:\temp\pas-ssl>

So the code for this was as follows :


/*
* Author: Pas Apicella
*/

package support.au.jdbc.ssl;

import java.security.Security;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import java.util.Date;
import java.util.Properties;

import oracle.jdbc.pool.OracleDataSource;


public class JDBCSSLWalletTrustore
{
private static OracleDataSource ods = null;
private static final String TRUSTSTORE =
"D:/jdev11g-boxer/jdeveloper/jdev/mywork/JDBCSSL11g/wallets/client/ewallet.p12";
private static final String TRUSTSTORETYPE = "PKCS12";
private static final String TRUSTSTOREPASSWORD = "myclient123";
private static final String DBURL =
"jdbc:oracle:thin:@(DESCRIPTION=(ADDRESS=(PROTOCOL=tcps)" +
"(HOST=beast.au.oracle.com)(PORT=2484))" +
"(CONNECT_DATA=(SERVICE_NAME=linux11g)))";

private static final String DBUSER = "scott";
private static final String DBPASSWD = "tiger";

public JDBCSSLWalletTrustore()
{
Security.addProvider(new oracle.security.pki.OraclePKIProvider());
}

public void run () throws SQLException
{
String sql = "select user from dual";
Statement stmt = null;
ResultSet rset = null;
Connection conn = null;

try
{
conn = getConnection();
stmt = conn.createStatement();
rset = stmt.executeQuery(sql);
rset.next();

if (rset != null)
{
System.out.println
(String.format("Conncted as DATABASE USER %s",
rset.getString(1)));
}
}
finally
{
if (stmt != null)
{
stmt.close();
}

if (rset != null)
{
rset.close();
}

if (conn != null)
{
conn.close();
}
}
}

public static Connection getConnection() throws SQLException
{
Properties props = new Properties();

props.setProperty("javax.net.ssl.trustStore",
TRUSTSTORE);
props.setProperty("javax.net.ssl.trustStoreType", TRUSTSTORETYPE);
props.setProperty("javax.net.ssl.trustStorePassword", TRUSTSTOREPASSWORD);

ods = new OracleDataSource();
ods.setUser(DBUSER);
ods.setPassword(DBPASSWD);
ods.setURL(DBURL);
ods.setConnectionProperties(props);

Connection conn = ods.getConnection();

return conn;
}

public static void main(String[] args)
{
System.out.println("Start: " + new Date());
JDBCSSLWalletTrustore testssl = new JDBCSSLWalletTrustore();

try
{
testssl.run();
}
catch (SQLException se)
{
System.out.println("SQL Exception occurred: ");
se.printStackTrace();
}
catch (Exception e)
{
System.out.println("Exception occurred: ");
e.printStackTrace();
}

System.out.println("Ended: " + new Date());
}


}

If you have trouble getting a JDBC THIN / SSL demo to work , it's worth tuning on this java option at run time to get more detailed information, here is what we get when we turn it on with the same code above, the output is far more detailed to enable you to debug why it may not be working for you.

-Djavax.net.debug=ssl

Output
-------

Start: Tue Jan 20 11:10:05 EST 2009
adding as trusted cert:
Subject: OU=Class 3 Public Primary Certification Authority, O="VeriSign, Inc.", C=US
Issuer: OU=Class 3 Public Primary Certification Authority, O="VeriSign, Inc.", C=US
Algorithm: RSA; Serial number: 0x70bae41d10d92934b638ca7b03ccbabf
Valid from Mon Jan 29 11:00:00 EST 1996 until Wed Aug 02 09:59:59 EST 2028

adding as trusted cert:
Subject: CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), O=Entrust.net
Issuer: CN=Entrust.net Certification Authority (2048), OU=(c) 1999 Entrust.net Limited, OU=www.entrust.net/CPS_2048 incorp. by ref. (limits liab.), O=Entrust.net
Algorithm: RSA; Serial number: 0x3863b966
Valid from Sat Dec 25 04:50:51 EST 1999 until Wed Dec 25 05:20:51 EST 2019

.....

.....

*** Finished
verify_data: { 196, 181, 216, 111, 12, 62, 126, 237, 120, 137, 1, 126 }
***
main, WRITE: TLSv1 Handshake, length = 48
main, READ: TLSv1 Change Cipher Spec, length = 1
main, READ: TLSv1 Handshake, length = 48
*** Finished
verify_data: { 50, 203, 187, 227, 95, 124, 39, 44, 151, 160, 209, 59 }
***
%% Didn't cache non-resumable client session: [Session-2, TLS_RSA_WITH_AES_128_CBC_SHA]
main, WRITE: TLSv1 Application Data, length = 224
main, READ: TLSv1 Application Data, length = 48
main, WRITE: TLSv1 Application Data, length = 192
main, READ: TLSv1 Application Data, length = 160
main, WRITE: TLSv1 Application Data, length = 64
main, READ: TLSv1 Application Data, length = 208
main, WRITE: TLSv1 Application Data, length = 2080
main, WRITE: TLSv1 Application Data, length = 176
main, READ: TLSv1 Application Data, length = 2032
main, READ: TLSv1 Application Data, length = 288
main, WRITE: TLSv1 Application Data, length = 192
main, READ: TLSv1 Application Data, length = 224
main, WRITE: TLSv1 Application Data, length = 1056
main, READ: TLSv1 Application Data, length = 1360
main, WRITE: TLSv1 Application Data, length = 48
main, READ: TLSv1 Application Data, length = 208
main, WRITE: TLSv1 Application Data, length = 112
main, READ: TLSv1 Application Data, length = 512
Conncted as DATABASE USER SCOTT
main, WRITE: TLSv1 Application Data, length = 48
main, READ: TLSv1 Application Data, length = 48
main, WRITE: TLSv1 Application Data, length = 32
main, called close()
main, called closeInternal(true)
main, SEND TLSv1 ALERT: warning, description = close_notify
main, WRITE: TLSv1 Alert, length = 32
Ended: Tue Jan 20 11:10:07 EST 2009

Thursday, 13 November 2008

Simple Example of Memory Profiling in JDeveloper 11g

In the 11g JDBC Developer's guide below it clearly specifies the following.

http://download.oracle.com/docs/cd/B28359_01/java.111/b31224/oraint.htm#i1058743

Important: The cursor associated with a REF CURSOR is closed whenever the statement object that produced the REF CURSOR is closed. Unlike in past releases, the cursor associated with a REF CURSOR is not closed when the result set object in which the REF CURSOR was materialized is closed.

So here is how I used JDeveloper memory profiler to verify that this indeed was the case.

1. new java class as follows


package pas.jdbc.refcursor;
import java.sql.*;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OracleDriver;
import oracle.jdbc.OracleTypes;

public class RefCursorExample
{
private boolean detailsDisplayed = false;
private Connection conn = null;

public RefCursorExample() throws SQLException
{
conn = getConnection();
}

public void run () throws SQLException
{
CallableStatement cstmt = null;
ResultSet cursor = null;

try
{
if (!detailsDisplayed)
{
displayDeatils(conn);
detailsDisplayed = true;
}

// Use a PL/SQL block to open the cursor
cstmt = conn.prepareCall
("begin open ? for select ename from emp; end;");

cstmt.registerOutParameter(1, OracleTypes.CURSOR);
cstmt.execute();
cursor = ((OracleCallableStatement)cstmt).getCursor(1);

// Use the cursor like a standard ResultSet
while (cursor.next ())
{
System.out.println (cursor.getString(1));
}
System.out.println("\n");
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
if (cursor != null)
{
cursor.close();
}
}


}

public void displayDeatils (Connection conn) throws SQLException
{
// Create Oracle DatabaseMetaData object
DatabaseMetaData meta = conn.getMetaData ();

// gets driver info:

System.out.println("\n=============\nDatabase Product Name is ... " +
meta.getDatabaseProductName());
System.out.println("\nDatabase Product Version is " +
meta.getDatabaseProductVersion());
System.out.println("\n=============\nJDBC Driver Name is ........ " +
meta.getDriverName());
System.out.println("\nJDBC Driver Version is ..... " +
meta.getDriverVersion());
System.out.println("\nJDBC Driver Major Version is " +
meta.getDriverMajorVersion());
System.out.println("\nJDBC Driver Minor Version is " +
meta.getDriverMinorVersion());
System.out.println("\n=============");
}

public static Connection getConnection() throws SQLException
{
String username = "scott";
String password = "tiger";
String thinConn = "jdbc:oracle:thin:@beast.au.oracle.com:1521:linux11g";
DriverManager.registerDriver(new OracleDriver());
Connection conn =
DriverManager.getConnection(thinConn, username, password);
conn.setAutoCommit(false);
return conn;
}

public static void main(String[] args) throws Exception
{
RefCursorExample refCursorExample = new RefCursorExample();
refCursorExample.run();

for (int i = 1; i <= 5; i++)
{
System.out.println("Run #" + i + "\n");
refCursorExample.run();
}

Thread.sleep(30000);

System.out.println("finished");
}
}



You can see in the run() method we don't call close on the statement itself which leads to a code leak.


finally
{
if (cursor != null)
{
cursor.close();
}
}


2. In the project edit the default profile by double clicking on the project and selecting "Run/Deug/Profile" and then select "Edit" for default profile, then select "Tools Settings/Profiler" and select the checkbox "Begin Use Case on application Startup"

3. Press Ok twice

Now we are ready to memory profile the application. As ou can see from the code we call the method run() 5 times so lets see if we can see the Statement leak.

4. Select Run -> Memory profile

A snapshot is taken as soon as the program runs, and then it sleeps for 30 seconds before finally completing. Click on the icon "End task" and set the class filter to "T4CStatement"






5. You will see 2 columns , one called "New" the other one called "Final Num"

Both are 6 which shows that we have 6 "T4CStatement" classes still in the heap.

Now lets fix the code to actually call close() on the statement in the finally block of the run() mnethod as the 11g JDBC Developer's guide suggests to.

if (cstmt != null)
{
cstmt.close();
}

6. Now profile the class "RefCursorExample" again and you will see that we no longer have a build up of "T4CStatement" as previously examined, when the statement was closed as well.

We have "New" as 6 and "Final Num" as 1 as expected.

Monday, 25 August 2008

How to do a "desc emp" from JDBC

If you ever try to do a "desc emp" from a JDBC program , that won't work as that's a SQL*Plus command only, but here is how to write an SQL statement to do that which can easily be converted into a JDBC program and used.

Firstly here is what we get when we describe the EMP table in SQL*Plus.

SCOTT@linux10g> desc emp;
Name Null? Type
----------------------------------- -------- ------------------------
EMPNO NOT NULL NUMBER(4)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2)

Now here is the SQL we can use to get the same output from an SQL query.


select
column_name as "Name",
decode(NULLABLE, 'Y', '', 'N', 'Not Null') as "Null",
data_type||decode(data_type,
'NUMBER', decode(DATA_PRECISION, NULL, '',
'('||DATA_PRECISION||','||data_scale||')'),
'VARCHAR2', '('||data_length||')',
'CHAR', '('||data_length||')') as "Type"
from user_tab_columns
where table_name = 'EMP'


The output of that is as follows from the query above , identical to a "desc emp", or close enough, couple more decodes will sort out a few minors issues , but seems to work for most tables I tested it against.

Name Null Type
------------------------------ -------- ------------------------------
EMPNO Not Null NUMBER(4,0)
ENAME VARCHAR2(10)
JOB VARCHAR2(9)
MGR NUMBER(4,0)
HIREDATE DATE
SAL NUMBER(7,2)
COMM NUMBER(7,2)
DEPTNO NUMBER(2,0)

So with a bind variable defined it would simply be as follows


select
column_name as "Name",
decode(NULLABLE, 'Y', '', 'N', 'Not Null') as "Null",
data_type||decode(data_type,
'NUMBER', decode(DATA_PRECISION, NULL, '',
'('||DATA_PRECISION||','||data_scale||')'),
'VARCHAR2', '('||data_length||')',
'CHAR', '('||data_length||')') as "Type"
from user_tab_columns
where table_name = ?


And then easily be integrated into a JDBC program, I did this over the web using a JSP page, which lists all user tables and adds a DESCRIBE button next to the table name to get the output above when clicked.

Monday, 30 June 2008

ORA-00932 Using the Collect Function with Oracle JDBC driver

Recently I tried to use the COLLECT function from 10g as follows from SQL*Plus which worked fine.

SQL> select COLLECT(ename) as emps from emp;

EMPS
--------------------------------------------------------------------------------
SYSTPUJ7P3Tw/Wq/gQLsKRlBJ8Q==('SMITH', 'ALLEN', 'WARD', 'JONES', 'MARTIN', 'BLAK
E', 'CLARK', 'SCOTT', 'KING', 'TURNER', 'ADAMS', 'JAMES', 'FORD', 'MILLER')

I then decided to use the same query in JDBC and got this error when trying to execute a query from a Statement object.

java.sql.SQLException: ORA-00932: inconsistent datatypes: expected NUMBER got -

It's obvious the return type needs to be something which the JDBC client can actually work with , so to get this to work I simply did this.

1. Create a TYPE to hold my array of Employee names.

CREATE OR REPLACE TYPE varchar2_enames AS TABLE OF VARCHAR2(50);

I then quickly ran a new query to ensure my TYPE was working correctly as shown below.

SQL> select CAST(COLLECT(ename) AS varchar2_enames) AS emps from emp;

EMPS
--------------------------------------------------------------------------------
VARCHAR2_ENAMES('SMITH', 'ALLEN', 'WARD', 'JONES', 'MARTIN', 'BLAKE', 'CLARK', '
SCOTT', 'KING', 'TURNER', 'ADAMS', 'JAMES', 'FORD', 'MILLER')

Note: You will se this time the return output is a table of varchar's which we can easily map to a JDBC array which we can then obtain without any errors.

2. JDBC code as follows, using the 10.2.0.4 JDBC driver


package pas.au;

import java.sql.*;
import oracle.jdbc.OracleDriver;

public class Test
{
public Test()
{
}

public void run ()
{
Connection conn = null;
Statement stmt = null;
ResultSet rset = null;

String queryStr = "select CAST(COLLECT(ename) AS varchar2_enames) AS emps from emp";

try
{
conn = getConnection();
driverDeatils(conn);
stmt = conn.createStatement();
rset = stmt.executeQuery(queryStr);
// only expecting one row
rset.next();

Array arr = rset.getArray(1);
String[] values = (String[])arr.getArray();
for( int i = 0; i < values.length; i++ )
System.out.println( "row " + i + " = '" + values[i] +"'" );

}
catch (SQLException e)
{
e.printStackTrace();
}
finally
{
try
{
if (conn != null)
conn.close();

if (stmt != null)
stmt.close();

if (rset != null)
rset.close();
}
catch (Exception e)
{
// bad luck
}
}

}

public void driverDeatils (Connection conn) throws SQLException
{
DatabaseMetaData dmd = conn.getMetaData();
System.out.println("DriverVersion: ["+
dmd.getDriverVersion()+"]");
System.out.println("DriverMajorVersion: ["+
dmd.getDriverMajorVersion()+"]");
System.out.println("DriverMinorVersion: ["+
dmd.getDriverMinorVersion()+"]");
System.out.println("DriverName: ["+
dmd.getDriverName()+"]");
}

public static Connection getConnection() throws SQLException
{
String username = "scott";
String password = "tiger";
String thinConn = "jdbc:oracle:thin:@beast:1522:linux10g";
DriverManager.registerDriver(new OracleDriver());
Connection conn = DriverManager.getConnection(thinConn,username,password);
conn.setAutoCommit(false);
return conn;
}

public static void main(String[] args)
{
Test test = new Test();
test.run();
}
}


3. Output as follows.

DriverVersion: [10.2.0.4.0]
DriverMajorVersion: [10]
DriverMinorVersion: [2]
DriverName: [Oracle JDBC driver]
row 0 = 'SMITH'
row 1 = 'ALLEN'
row 2 = 'WARD'
row 3 = 'JONES'
row 4 = 'MARTIN'
row 5 = 'BLAKE'
row 6 = 'CLARK'
row 7 = 'SCOTT'
row 8 = 'KING'
row 9 = 'TURNER'
row 10 = 'ADAMS'
row 11 = 'JAMES'
row 12 = 'FORD'
row 13 = 'MILLER'