Calling PL/SQL package code in a Java Program - java

I am trying to call a procedure defined with a PL/SQL package in a Java program.
I am aware one can call stored procedures using connection.prepareCall in Jdbc. But there is very little information out there on how to call a procedure within a package.
I am at a stage in development where i am still considering what db framework to use. Just wondering what are the pros and cons of using JDBC for PLSQL ? For this usecase are there better alternatives to JDBC ?

Follow the simple steps below:
public static final String SOME_NAME = "{call schema_name.org_name_pkg.return_something(?,?)}"; // Change the schema name,packagename,and procedure name.
// Simple JDBC Connection Pooling
// Here I am passing param companyId which is IN param to stored procedure which will return me some value.
Connection conn = null;
CallableStatement stmt = null;
ResultSet rset = null;
try {
conn = DriverManager.getConnection("jdbc:mysql://hostname:port/dbname","username", "password");
stmt = conn.prepareCall(SOME_NAME);//We have declared this at the very top
stmt.setString(1, companyid);//Passing CompanyID here
stmt.registerOutParameter(2, OracleTypes.CURSOR);//Refcursor selects the row based upon query results provided in Package.
stmt.execute();
rset = (ResultSet) stmt.getObject(2);
while (rset.next()) {
String orgId=rset.getString("RPT_ORG_ID");
// When using refcursor easy to get the value just by using Column name
String orgName=rset.getString("RPT_ORG_NAME");
// Some Logic based what do you want to do with the data returned back from query
} catch (Exception e) {
LOGGER.error("Error extracting ", e);
} finally {
DBUtils.cleanUp(conn, stmt, rset);
}
// Clean and close you connection

Related

Correct way of calling reference cursors in PostgreSQL 11 and above using JDBC driver

I need some suggestion on how to get the data through PostgreSQL JDBC driver from stored procedures using reference cursors.
Since PostgreSQL 11, it supports stored procedures with create procedure command instead of create function.
I have a scenario in which I want to fetch data from a stored procedure using a reference cursor.
My stored procedure SQL looks like as shown below
CREATE OR REPLACE PROCEDURE public.pr_sampleuser(
p_firstuser character varying,
INOUT p_qusers refcursor)
LANGUAGE 'plpgsql'
AS $BODY$
BEGIN
OPEN p_qusers FOR
SELECT first_name,last_name,address
FROM public.test_user
WHERE UPPER(first_name) = UPPER(p_firstuser);
END;
$BODY$;
When we want to fetch the data using the JDBC driver, the first thing we need to add to the connection string is escapeSyntaxCallMode=call.
Following is the code-snippet that I am using to fetch the data,
try {
Properties props = new Properties();
props.setProperty("escapeSyntaxCallMode", "call");
Connection conn = DriverManager.getConnection(url,props);
String storedProc = "{call public.pr_sampleuser(?,?)}";
CallableStatement cs = conn.prepareCall(storedProc);
cs.setString(1,"Umesh");
cs.setObject(2,null);
cs.registerOutParameter(2,Types.REF_CURSOR);
conn.setAutoCommit(false);
// run StoredProcedure
cs.execute();
// get refcursor and convert it to ResultSet
ResultSet resultSet = (ResultSet) cs.getObject(2);
while (resultSet.next()) {
String firstName = resultSet.getString("first_name");
String lastname = resultSet.getString("last_name");
String address = resultSet.getString("address");
System.out.println(firstName);
System.out.println(lastname);
System.out.println(address);
}
} catch (SQLException e) {
System.err.format("SQL State: %s\n%s", e.getSQLState(), e.getMessage());
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
In this I am passing the second parameter as null using
cs.setObject(2,null);
I wanted to check if this is the correct way to fetch or if there is any better way to get the data.

H2 Database result set is readonly

I'm getting the SQLNonTransientException error when trying to update one of my rows in a H2 database.
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
//read data from database
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM BCSTASKS_SERVICE");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
if(rs.getString("Status").equals("Neu") && rs.getBoolean("wasShown") == false) {
rs.updateBoolean("WASSHOWN", true);
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
The error message already suggests that I should use conn.createStatement and set the ResultSet to CONCUR_UPDATABLE. The error occurs at the line with rs.updateBoolean(...);
Error Message:
The result set is readonly. You may need to use conn.createStatement(.., ResultSet.CONCUR_UPDATABLE). [90140-210]
The problem is I don't know where and how I should use this method. In the same function or at the start of the program?
Most DB code I see doesn't attempt to use the fact that resultsets are updatable, and will instead fire off an additional UPDATE query, which works fine.
However, sure, H2 supports updateable resultsets too. However, some of the features that ResultSets have actually have quite a cost; the DB engine needs to do a boatload of additional bookkeeping to enable such features which have a performance cost. Lots of database queries are extremely performance sensitive, so by default you do not get the bookkeeping and therefore these features do not work. You need to enable them explicitly, that's what the error is telling you.
You're currently calling the 'wrong' preparedStatement method. You want the more extended one, where you pick and choose which additional bookkeeping you want H2 to do for you, in order to enable these things. You want this one.
conn.prepareStatement(
"SELECT * FROM BCSTASKS_SERVICE",
ResultSet.TYPE_SCROLL_INSENSITIVE, // [edited]
ResultSet.CONCUR_UPDATABLE);
That CONCUR_UPDATABLE thing is just a flag you pass to say: Please do the bookkeeping so that I can call .update.
[edited] This used to read 0 before, but as #MarkRotteveel pointed out, that's not valid according to the documentation.
You have to put update query for update data in database but you are going with select query that is the problem.
Select query is used if you have to fetch data from database.
Update query is used for update data in database where data already stored in database but you just overwrite data.
Here down is modified code:
public static void setNewServiceInformationsToShown() {
try (Connection conn = DriverManager.getConnection("jdbc:h2:" + Main.config_db_location,
Main.config_db_username, Main.config_db_password)) {
PreparedStatement stmt = conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET wasShown = ? WHERE status = ? AND wasShown = ?");
stmt.setBoolean(1, true);
stmt.setString(2, "Neu");
stmt.setBoolean(3, false);
stmt.executeUpdate();
stmt.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
You need to create a separate query/prepareStatement for an update. In your case as far as I can see you need only one update query:
conn.prepareStatement("UPDATE BCSTASKS_SERVICE SET WASSHOWN=true where
Status = 'Neu' and wasShown = false "

Working with a saved Access parameter query under (JDBC-)ODBC

In my Java application I have to use data that comes from an Access 2010 database. I used the graphical query creator from Access to create the appropriate query and it works great.
Unfortunately, when I try to use a prepared statement with that query (in order to use a parameter) in my Java application I got an NPE
messageChildrenRequest.setString(1, blockId);
ResultSet result = messageChildrenRequest.executeQuery();
The NPE occurs when i set the parameter with setString() and my query is not execute but when i look with the debugger the statement is not null...
My query given by access is :
SELECT IRSIDD.[BLOCK ID], IRSIDD.[IDENTIFICATION CHIFFREE], IRSIDD.MSG_ID, MAIN.SUB_FIELD_ID, MAIN.ORDER, FIELD.[FIELD NAME], FIELD.TYPE, FIELD.[RC 'TYPE] "
FROM IRSIDD LEFT JOIN (MAIN LEFT JOIN FIELD ON MAIN.SUB_FIELD_ID = FIELD.[FIELD ID]) ON IRSIDD.[BLOCK ID] = MAIN.BLOCK_ID "
WHERE ((IRSIDD.[BLOCK ID])=?)
The StackTrace gives me :
Exception in thread "main"
java.lang.NullPointerException
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.clearParameter(Unknown Source)
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.setChar(Unknown Source)
at sun.jdbc.odbc.JdbcOdbcPreparedStatement.setString(Unknown Source)
When I tried a very simple prepared statement :
SELECT * FROM table1 WHERE table1.id = ?
I didn't get any NPE when setting the parameter so I suspect that Access and java JDBC do not have the same way to deal with join.
Does someone already that kind of problem or can confirm that the structure of my query is the problem here?
Connection connection = null;
CallableStatement callStmt = null;
String myParam = "test";
String statement = "SELECT * FROM table1 WHERE table1.id = ?";
try {
connection = DatabasePoolUtil.getDefaultConnection(); //Connects
callStmt = connection.prepareCall(statement);
callStmt.setString(1,myParam);
callStmt.execute();
}
catch (SQLException ex) {
// Do something
}
finally { // connection has to be closed
if (callStmt != null) {
callStmt.close();
}
if (connection != null) {
connection.close();
}
}
The ODBC (and OLEDB) interfaces to an Access database expose different types of saved Access queries as either "Views" or "Stored Procedures":
Access query appears under ODBC/OLEDB as
------------------------------- ---------------------------
Select query without parameters View
Select query with parameters Stored Procedure
Append query (INSERT) Stored Procedure
Update query Stored Procedure
Delete query Stored Procedure
Since your Access saved query has parameters it will look like a Stored Procedure under ODBC and therefore you need to use a CallableStatement to work with it.
For example, given the following saved parameter query named [myParameterQuery] in Access
PARAMETERS specificID Long;
SELECT Table1.*
FROM Table1
WHERE (((Table1.ID)=[specificID]));
we need to use the following Java code to retrieve the row for ID=3:
String connectionString = "jdbc:odbc:"
+ "DRIVER={Microsoft Access Driver (*.mdb, *.accdb)};"
+ "DBQ=C:/Users/Public/32224442.accdb;";
try (Connection conn = DriverManager.getConnection(connectionString)) {
try (CallableStatement cs = conn.prepareCall("{call myParameterQuery(?)}")) {
cs.setInt(1, 3); // set "specificID" parameter to 3
try (ResultSet rs = cs.executeQuery()) {
rs.next();
System.out.println(rs.getInt(1));
}
}
} catch (Exception e) {
System.err.println(e.getMessage());
System.exit(0);
}
The corresponding C# code would be:
string myConnectionString =
#"Driver={Microsoft Access Driver (*.mdb, *.accdb)};" +
#"Dbq=C:\Users\Public\32224442.accdb;";
using (var con = new OdbcConnection(myConnectionString))
{
con.Open();
using (var cmd = new OdbcCommand("{CALL myParameterQuery (?)}", con))
{
cmd.CommandType = System.Data.CommandType.StoredProcedure;
cmd.Parameters.Add("?", OdbcType.Int).Value = 3; // set "specificID" parameter to 3
using (OdbcDataReader rdr = cmd.ExecuteReader())
{
rdr.Read();
Console.WriteLine(rdr[0]);
}
}
}

How to Pass Multiple Timestamps as Dynamic Parameters into a Derby Query?

I'm converting a Java application from PostGresSQL to Derby (10.10.1.1). The PG database has many procedures that ideally will transfer to Derby procedures.
One of the PG stored procedures passes an array of Timestamps, similar to this Procedure/SQL:
CREATE FUNCTION getDownloads(_download_array timestamp without time zone[])
LANGUAGE plpgsql AS $$
DECLARE mycurs refcursor;
BEGIN
SELECT * FROM download_time d
WHERE d.downloadtime = ANY(_download_array);
END
RETURN mycurs;
Derby procedures are basically declarations that reference your procedures class that contains public static Java methods. The methods typically use the java.SQL PreparedStatement object, and may contain dynamic parameters. The procedure is called via the java.SQL CallableStatement object, with set param values, executed to return a ResultSet.
I would like to translate the above PG procedure into a Derby procedure that accepts multiple Timestamp values, possibly using the ANY or IN statements. In limited searches, it appears that Derby does not support arrays as dynamic parameters.
Using the Squirrel SQL client, this syntax proves acceptable:
SELECT * FROM download_time d
WHERE d.downloadtime
IN('2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0', '2014-05-06 07:08:09.0')
However in practice, passing comma-delimited Timestamps to the IN or ANY statements does not work, pseudo-code below:
try {
Connection conn = getConnection();
CallableStatement cstmt = null;
cstmt = conn.prepareCall("{ call getDownloads(?) }");
cstmt.setTimestamp(3, "'2011-11-13 13:24:00.0', '2011-11-13 13:28:00.0'");
//Also tried this:
cstmt.setString(3, "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0");
cstmt.execute();
rs = cstmt.getResultSet();
while (null != rs && rs.next()) {
...
}
} catch (SQLException sqle) {
...handle errors
}
Following the above examples, this error occurs:
java.sql.SQLException:
The syntax of the string representation of a date/time value is incorrect.
I'm in search of alternative methods, and am considering solutions I've found in an excellent article on StackOverflow, PreparedStatement IN clause alternatives?
I would be willing to consider simply writing dynamic SQL instead of a parameterized procedure, but the real query is rather beastly. :)
Since no one offered an answer, I'm posting my solution to the problem. The solution is to pass a String variable, "downloadTimes" containing concatenated date/times in a comma-delimited-like format. For brevity, the NULL-check condition was excluded. If a NULL is passed, that line is simply excluded.
Here is the procedure:
public static void getDownloads(int theId, String downloadTimes, ResultSet[] rs)
throws SQLException {
String DML = null;
PreparedStatement ps = null;
DML = "SELECT d.* FROM download_time d WHERE d.id = ? " +
"AND d.downloadtime IN(" + downloadTimes + ") " : "") + //Add chk null condition
"ORDER BY 1, 2 DESC, 3 ";
ps = conn.prepareStatement(DML);
ps.setInt(1, theId);
rs[0] = ps.executeQuery();
}
Note that the "getDownloads" procedure is declared in Derby later in the same class (see declaration in my original question), left out for simplicity. The procedure is called by a method in a different class:
public Map<GregorianCalendar, List<Fault>> getDownloadFaultList(
Integer theId, String subsystem, List<GregorianCalendar> downloadTimes) {
CallableStatement cstmt = null;
ResultSet rs = null;
String downloadCalListToCsv = null;
// parseGregorianCalListToCsv() creates a CSV string out of dates.
// I.e., "2011-11-13 13:24:00.0, 2011-11-13 13:28:00.0"
if (false == downloadTimes.isEmpty()) {
downloadCalListToCsv = DataTypeConverter
.parseGregorianCalListToCsv(downloadTimes, timestampFormat);
}
try {
cstmt = getConn().prepareCall("{ call getDownloads(?, ?) }");
// Register the parameters
cstmt.setInt(1, theId);
// Get timezone from first entry, assuming all same timezone
if (! downloadTimes.isEmpty()) {
cal.setTimeZone(downloadTimes.get(0).getTimeZone());
}
cstmt.setString(2, downloadCalListToCsv);
cstmt.execute();
rs = cstmt.getResultSet();
while (null != rs && rs.next()) {
//Use the download timestamps here
}
} catch (SQLException sqle) {
//error handling here
} finally {
//Close resources
close(rs, cstmt);
}
return faultMap;
}
The solution is not elegant, but works in practice.

Error on calling a stored procedure from java - Invalid JDBC data type -10

I have a java class from which i am connecting to the SQL Server 2008 which is on a remote machine.I Can retrieve data using a class which means the connection is working fine.
But when i try to run a stored procedure it gives me
Exception as com.microsoft.sqlserver.jdbc.SQLServerException - Invalid JDBC data type -10.
Find the code below. Can anyone help me with this? I have added the sqljdbc, sqljdbc4 jars to my class buildpath.
CallableStatement cs=null;
con = SqlConnectionManager.getConnection();
cs = con.prepareCall("{call dbo.PKG_ER_SEL.sp_ERGetJobCode(?,?,?)}");
cs.setInt(1, orgId);//v_org
cs.setString(2, compCode);//v_type
cs.registerOutParameter(3, OracleTypes.CURSOR); //getting exception here
cs.executeUpdate();
ResultSet resultSet = (ResultSet) cs.getObject(3);
if (resultSet != null) {
while ( resultSet.next() ) {
JobVO obj = new JobVO();
obj.setCode(resultSet.getString("CODE"));
obj.setName(resultSet.getString("NAME"));
seqAttList.add(obj);
}
resultSet.close();
}
}catch(Exception e){
e.printStackTrace();
}finally{
SqlConnectionManager.releaseConnection(con);
}
Thanks
In JAVA get the ResultSet from the call to executeQuery on the Statement object. The ResultSet instance is the equivalent of a CURSOR in T-SQL. Read some more about using ResultSet here.
For SQL Server you call/execute a stored procedure as detailed here. A simple example would be EXECUTE my_stored_procedure if your stored procedure is called my_stored_procedure.

Categories