Calling Sybase Adaptive Server Enterprise's "sp_help" from JDBC - java

In order to query the database meta data in Sybase ASE, I found this relevant answer (not the accepted one), to be ideal:
From a Sybase Database, how I can get table description ( field names and types)?
Unfortunately, I can't seem to find any documentation, how I'm supposed to call sp_help from JDBC. According to the documentation, sp_help returns several cursors / result sets. The first one contains information about the table itself, the second one about the columns, etc. When I do this:
PreparedStatement stmt = getConnection().prepareStatement("sp_help 't_language'");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getObject(1));
// ...
}
I only get the results from the first cursor. How to access the other ones?

When you have multiple result sets you need to use the execute() method rather than executeQuery().
Here's an example:
CallableStatement cstmt;
ResultSet rs;
int i;
String s;
...
cstmt.execute(); // Call the stored procedure 1
rs = cstmt.getResultSet(); // Get the first result set 2
while (rs.next()) { // Position the cursor 3
i = rs.getInt(1); // Retrieve current result set value
System.out.println("Value from first result set = " + i);
// Print the value
}
cstmt.getMoreResults(); // Point to the second result set 4a
// and close the first result set
rs = cstmt.getResultSet(); // Get the second result set 4b
while (rs.next()) { // Position the cursor 4c
s = rs.getString(1); // Retrieve current result set value
System.out.println("Value from second result set = " + s);
// Print the value
}
rs.close(); // Close the result set
cstmt.close(); // Close the statement

You also need to call getUpdateCount() as well as getMoreResults() to read the entire result set. Here is some code I used to call sp_helpartition to retrieve partition information from a SYBASE DB.
try {
connection = getPooledConnection(poolName);
statement = connection.createStatement();
CallableStatement callable = connection.prepareCall(
"{ call sp_helpartition(?) }");
callable.setString(1,tableName);
callable.execute();
int partitions = 0;
/*
* Loop through results until there are no more result sets or
* or update counts to read. The number of partitions is recorded
* in the number of rows in the second result set.
*/
for (int index = 0 ; ; index ++){
if (callable.getMoreResults()){
ResultSet results = callable.getResultSet();
int count = 0 ;
while (results.next()){
count++;
}
if (index == 1){
partitions = count;
}
} else if (callable.getUpdateCount() == -1){
break ;
}
}
return partitions ;
} catch (Exception e) {
return 0 ;
} finally {
statement.close();
connection.close();
}

Thanks to Martin Clayton's answer here, I could figure out how to query Sybase ASE's sp_help function generically. I documented some more details about how this can be done in my blog here. I worked support for multiple JDBC result sets into jOOQ. In the case of sp_help, calling that function using the jOOQ API might look like this:
Factory create = new ASEFactory(connection);
// Get a list of tables, a list of user types, etc
List<Result<Record>> tables = create.fetchMany("sp_help");
// Get some information about the my_table table, its
// columns, keys, indexes, etc
List<Result<Record>> results = create.fetchMany("sp_help 'my_table'");

Related

Why does my for loop in android studio stop after the first iteration?

I created a system in which I can run all my postgre sql queries with only one single Async Task Class in Android Studio. This was really(!!) challenging due to the big amount of limitations that I had to face. But this works actually really great!
//Used for connecting to database and executing queries.
//Index 0 of input string must be the query, Index 1 must be the tablename we demand
//We can only gather data from 1 table for each query, so if you need data from several tablecolumns, use multiple queries like:
//[0] = query, [1] = tablename, [2] = 2nd query, [3] = 2nd tablename, [4] = 3rd query, [5] = 3rd table name ... and so on (each query must come with a tablename)
public class DBHandler extends AsyncTask<String, Void, List<String>>
{
public AsyncResponse delegate;
#Override
protected List<String> doInBackground(String...query)
{
List<String> result = new ArrayList<String>();
String sql;
String tableresult = null;
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
Class.forName("org.postgresql.Driver");
conn = DriverManager.getConnection("jdbc:postgresql://192.168.200.300:5439/dbname?user=anonymous&password=secretpw");
st = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE); //necessary if you want to use rs.first() after rs.next(), it makes the resultset scrollable
for (int i = 0; i <= query.length-1; i = i+2) //queries are always stored in i=0 and/or in i+2, because i+1 contain the demanded tablenames for resultset handling
{
System.out.println("I is: " +i);
if (!query[i].isEmpty())
{
System.out.println(query[i]);
sql = query[i];
rs = st.executeQuery(sql);
while (rs.next())
if (!query[i + 1].isEmpty() || !rs.getString(query[i + 1]).isEmpty()) //if i+1 is empty, there is no demanded tablename. Used when we dont need any return values (ie. INSERT, UPDATE)
result.add(rs.getString(query[i + 1])); //demanded tablename is always stored in i+1
//We add an empty entry if we demand multiple tablenames so we can keep them seperate
//Might be replaced with any other char, but you will have to backtrack all usages of DBHandler and fix the filters there
if(i+2 < query.length)
result.add(" ");
}
rs.first(); //reset pointer for rs.next()
}
rs.close();
st.close();
conn.close();
System.out.println("End of AsyncTask");
}
catch (SQLException ex)
{
ex.printStackTrace();
}
catch (Exception e)
{
e.printStackTrace();
}
return result;
}
//onPostExecute returns query result in a List.
//We need to use interaces delegate feature to send the result to other classes, like "Auslieferung", which is implementing the interface
#Override
protected void onPostExecute(List<String> result)
{
super.onPostExecute(result);
System.out.println("Result: " +result.toString());
if (!result.isEmpty())
delegate.processFinish(result);
}
}
There is a for-loop in this Async Task.
for (int i = 0; i <= query.length-1; i = i+2)
And now finally I can explain my issue:
I usually use SELECT queries, sometimes I use an INSERT query (which can be done by a single query), but when I parse an Update Query, my for-loop stops iterating after the first pass, so i+2 never happens. The update queries look like this:
String updatequeries[] = {UPDATE delivery SET contactperson = 'Jon Doe' WHERE officeid = 5, " ", UPDATE delivery SET contactemail = 'abd#def.gh' WHERE officeid = 5, " "};
Why does this for loop stop running right after the first run? The debugger does not show anything unusual, everything was parsed right and there are no queries missing. Updating a table does not return any results, but nothing depends on result values here. I tried to run 20 update queries in a single string var, but the for loop stops after the first iteration anyway. No issues are displayed in the debugger or in the logs. Have I overseen something or is there anything I don't know? Might this be a bug? Please help me! This issue drives me crazy.

Query through multiple resultSet output from a Stored Procedure to get the value of a column

A stored Procedure is returning three Result sets.I want a value from of a column from the third result Set.I would like to loop through the result sets and get the value of that column.
Ex: lets say SP returns three tables.
table 1:
Sno,Col_val1,Col_Val 2
table2:
Sno,Col_val11,Col_Val 22
table 3:
Sno,Col_val111,Col_Val 222
the above are output of a SP.
i want the value of Col_val111.how to do the following in java?
This might help. The example was given here but reproduced below in case of link rot:
CallableStatement cstmt;
ResultSet rs;
int i;
String s;
...
cstmt.execute(); // Call the stored procedure
rs = cstmt.getResultSet(); // Get the first result set
while (rs.next()) { // Position the cursor
i = rs.getInt(1); // Retrieve current result set value
System.out.println("Value from first result set = " + i);
// Print the value
}
cstmt.getMoreResults(); // Point to the second result set
// and close the first result set
rs = cstmt.getResultSet(); // Get the second result set
while (rs.next()) { // Position the cursor
s = rs.getString(1); // Retrieve current result set value
System.out.println("Value from second result set = " + s);
// Print the value
}
rs.close(); // Close the result set
cstmt.close(); // Close the statement
You can get to the next result set using Statement.getMoreResults() and Statement.getResultSet.
Statement stmt = // prepare the statement
stmt.execute();
for (int i = 1; i < 3; i++) {
stmt.getMoreResults();
}
thirdResultset = stmt.getResultSet();
// process the third resultset
... plus a ton of code to check that all is going well.
This is the code which i made just for fetching the column value of the last table.Thanks nick and Jiri.
CallableStatement cal = null;
String sp_call = "{call "+SP_Name+" (?,?,?)" +"}";
cal= connection.prepareCall(sp_call);
cal.setString(1,param1);
cal.setInt(2,param2);
cal.setInt(3,param3);
ResultSet res =null;
cal.execute();
int rs_count = 0;
while(rs_count<3)
{
if(i<2){cal.getMoreResults();rs = cal.getResultSet();rs_count++;continue;}
else{return rs; }
}

Expecting multiple ResultSets, but only get one

I have a stored procedure which returns multiple result sets as follows,
create proc test as
begin
select '1'
select a,b into #temp from TABLE1
select * from #temp
select '2'
select 'Done'
end
And my java call is,
CallableStatement stmt = null;
String procString= "EXEC test";
stmt = conn.prepareCall(procString);
boolean results = stmt.execute();
System.out.println(results);
do {
if(results) {
rs = stmt.getResultSet();
while (rs.next()) {
System.out.println(rs.getString(1) + ", " );
}
rs.close();
}
System.out.println();
results = stmt.getMoreResults();
System.out.println("results - "+results);
} while(results);
So as per the above snippet, the output should have all the 4 selects. But I just get the first select and nothing else. I just removed the second select which does insert into temp table, after which I get all the 4 selects.
Why does this happen?
You are misinterpreting the meaning of the boolean returned by getMoreResults:
There are no more results when the following is true:
// stmt is a Statement object
((stmt.getMoreResults() == false) && (stmt.getUpdateCount() == -1))
Returns:
true if the next result is a ResultSet object; false if it is an update count or there are no more results
As you haven't got a SET NOCOUNT ON in your stored procedure, the SELECT ... INTO ... will generate an update count. This will be returned after the first select. Only when getMoreResults returns false and getUpdateCount returns -1 can you be sure that there are no more results.
Your current code will exit the do .. while as soon as getMoreResults has returned false. You either need to add SET NOCOUNT ON to your stored procedure, or process multiple results taking into account the update counts.
To correctly process multiple results with update counts you need to do something like:
PreparedStatement pstmt = connection.prepareStatement(...);
// ...
boolean result = pstmt.execute();
while(true)
if (result) {
ResultSet rs = pstmt.getResultSet();
// Do something with resultset ...
} else {
int updateCount = pstmt.getUpdateCount();
if (updateCount == -1) {
// no more results
break;
}
// Do something with update count ...
}
result = pstmt.getMoreResults();
}
I copied the above from my answer to another question, it is similar but not exactly the same. The rest of that answer might provide some more details: Java SQL: Statement.hasResultSet()?

get one result at a time from database (when more than one result exists)

I have a database in which more than one row has the same value.
Eg,
Column1 Column2
A X
A Y
A Z
A resultset gets all the values of column 2 as a single string.
How to get each unique value from database (or from result set)?
(I use java)
Thanks in advance.
Have a look at
SELECT columna ,
(SELECT GROUP_CONCAT(DISTINCT columnb SEPARATOR ' ') FROM test
WHERE columna=t.`columna` ) as columnb_string
FROM `test` t GROUP BY columna
Here is Fiddle Example
What is the query/code you are using to get all of column two as a string? It is always a good idea to test your query in the database system itself or you could use SQLFiddle to ensure you are getting the results you are expecting from your query
From the Doc:
A ResultSet object maintains a cursor pointing to its current row of data. Initially the cursor is positioned before the first row. The next method moves the cursor to the next row, and because it returns false when there are no more rows in the ResultSet object, it can be used in a while loop to iterate through the result set.
Read the rest of Java Doc for ResultSet
A typical approach to getting values from the data base is to
//Construct a statement
String yourStatement = "Select COLUMN_B from aTable WHERE COLUMN_A LIKE 'A'" // EDITED
PreparedStatement aPrepStatement = null;
ResultSet rs = null;
List<String> aList = new ArrayList<String>(); // ADDED
try
{
aPrepStatement = yourConnection.prepareStatement(yourStatement)
rs = aPrepStatement.executeQuery();
while(rs.next())
{
// You are now in the first row of data depending on your query
// String column1 = rs.getString(1); // 'A' In the table you descrbed above (for first iteration
String column2 = rs.getString(1); // 'X' In the table you descrbed above
// Now do what you need to with the data
aList.add(column2) // EDITED
}
}
catch (Exception ex) {
// deal with any exceptions that arise
ex.printStackTrace();
}
finally
{
//Close your Resultset and prepared statement (this will possibly need a try catch block also
if(rs != null)
rs.close();
if(aPrepStatement != null)
aPrepStatement.close();
}
// ADDED BELOW
for(String s: aList)
System.out.println(s) // should give output
// X
// Y
// Z
Doc for PreparedStatement and Connection

Unable to get multiple Table entities through Stored procedure using hibernate

Here is my stored Procedure
Create PROCEDURE [dbo].getUserAndEnum
AS
BEGIN
select * from user_master where id =1
select * from enum_master where id = 1
End
With hibernate i written
Session session = HibernateFactory.getSessionFactory().openSession();
Transaction tr = session.beginTransaction();
SQLQuery qr=session.createSQLQuery("getUserAndEnum");
List list = qr.list();
In list i am getting only the user object ..what about my enum_master row with id 1
P.S enum_master row with id 1 is there in DB
Thanks.
'Rules/limitations for using stored procedures' in hibernate documentation states that
"The procedure must return a result set. Note that since these servers can return multiple result sets and update counts, Hibernate will iterate the results and take the first result that is a result set as its return value. Everything else will be discarded."
(reference : http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/querysql.html#sp_query)
As stated, the second resultset in your case is being ignored.
You would need to use jdbc for getting both result sets. Either you can make separate classes for doing so, or alternatively, hibernate offers you methods for performing traditional jdbc operations via its session's 'doWork' and 'doReturningWork' methods...
A simple example could be:
List<Object> res = session.doReturningWork(new ReturningWork<List<Object> /*objectType returned*/>() {
#Override
/* or object type you need to return to process*/
public List<Object> execute(Connection conn) throws SQLException
{
CallableStatement cstmt = conn.prepareCall("CALL YOUR_PROCEDURE");
//Result list that would return ALL rows of ALL result sets
List<Object> result = new ArrayList<Object>();
try
{
cstmt.execute();
ResultSet rs = cstmt.getResultSet(); // First resultset
while (rs.next()) {//Read items/rows of first resultset
// .
// Process rows of first resultset
result.add(obj); // add items of resultset 1 to the returning list object
}
cstmt.getMoreResults(); // Moves to this Statement object's next result, returns true if it is a ResultSet object
rs = cstmt.getResultSet(); // Second resultset
while (rs.next()) {
// .
// Process rows of second resultset
result.add(obj); // add items of resultset 2 to the returning list object
}
rs.close();
}
finally
{cstmt.close();}
return result; // this should contain All rows or objects you need for further processing
}
});

Categories

Resources