Invalid Column Index with PL/SQL Functions - java

I am trying to make a simple PL/SQL-to-Java setup, where I make a function in PL/SQL and call it from Java, expecting a return value.
I am fairly new to PL/SQL and calling it in Java so my approach is probably just lacking understanding in how return values from SQL actually works.
My SQL Function looks like this:
CREATE OR REPLACE FUNCTION GET_BOOKED_COUNT(P_NO VARCHAR2)
RETURN NUMBER AS
RESULTS NUMBER := 0;
BEGIN
SELECT COUNT(*) INTO RESULTS FROM SEAT WHERE PLANE_NO=P_NO AND BOOKED IS NULL;
RETURN RESULTS;
END GET_BOOKED_COUNT;
Return a number pretty much. It needs to check a table of seats to see how many of them are not booked yet.
After trying to follow various other Stackoverflow posts, I tried to duplicate what they did but it keeps telling me that the Index Column is Invalid.
public static boolean isAllBooked(String plane_no) {
String sql = "{? = CALL GET_BOOKED_COUNT('?')}";
boolean res = false;
try {
Connection conn = getConnection("db_010", "db2014");
CallableStatement cs = conn.prepareCall(sql);
cs.setString(2, plane_no);
cs.registerOutParameter(1, Types.NUMERIC);
cs.executeUpdate();
int i = cs.getInt(1);
if(i == 0) {
res = true;
}
conn.close();
} catch (SQLException ex) {
Logger.getLogger(HelperClass.class.getName()).log(Level.SEVERE, null, ex);
}
return res;
}
It's probably an easy fix, so what am I doing wrong exactly?

Your second bind variable, the argument to the function, should not be enclosed in single quotes. You're passing the literal value '?', not defining a bind:
String sql = "{? = CALL GET_BOOKED_COUNT(?)}";
The way you then refer to both parameters seems to be OK.

Related

How do I turn my INT (id) into a String in my return from MySQL?

I'm trying to my a very simple webapplication, webshop, for cupcakes.
From the webApp you can choose a cupcake form the dropdown with three attributes
(top, bottom, quantity). These are stored in an ArrayList on my sessionScope but all in numbers e.g. Chokolate as 1 and Vanilla as 2. I want to use these topId numbers to ask my DB (MySQL) for what is in 1 and then have it return Chokolate.
I think I am almost there with my code, but can't get it to return my String, as my topId is an Int.
public static Top getTopById(int topId) {
readFromArrayPutInSQL();
String sql = "INSERT INTO cupcaketopping (toppingType, toppingPrice) VALUES (?, ?)";
try {
ConnectionPool connectionPool = new ConnectionPool();
String query = "SELECT toppingType FROM cupcaketopping";
Statement statement = connectionPool.getConnection().createStatement();
ResultSet rs = statement.executeQuery(query);
rs.getString(topId);
} catch (SQLException e) {
throw new RuntimeException(e);
}
return topId; //Here is the problem - I GUESS?
}
Code after changes due to input in comments, seem to be working!
public static Top getTopById(int topId) {
readFromArrayPutInSQL();
String query = "SELECT toppingType FROM cupcaketopping WHERE toppingID = "+topId+"";
try {
ConnectionPool connectionPool = new ConnectionPool();
PreparedStatement preparedStatement = connectionPool.getConnection().prepareStatement(query);
ResultSet rs = preparedStatement.executeQuery(query);
rs.next();
return new Top(rs.getString(1));
//connectionPool.close(); //NOTE! Won't run, IntelliJ is asking me to delete!
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
There are a few problems:
You're selecting all rows from the cupcaketopping table, regardless of the topId. You should probably be using a PreparedStatement, and then use topId as part of your query.
You never call ResultSet#next(). The result set always starts "before" the first row. You have to call next() for each row in the result set (it returns true if there is a row to read).
The ResultSet#getString(int) method gets the String value of the column at the given index of the result. You only select one column, so the argument should probably be 1 (not topId).
You never close the Statement when done with it.
Depending on how your connection pool class works, you might actually need to close the Connection instead.
You never try to use the String returned by rs.getString(topId).
You never try to convert the query result to a Top instance.
Given it's possible the query will return no result, you might want to consider making the return type Optional<Top>.
The sql string seems to have no purpose.
Your code should look more like this:
public Optional<Top> getTopById(int topId) {
Connection conn = ...;
String query = "SELECT toppingType FROM cupcaketopping WHERE id = ?";
// closes the statement via try-with-resources
try (PreparedStatement stat = conn.prepareStatement(query)) {
stat.setInt(1, topId);
ResultSet rs = stat.executeQuery();
// assume unique result (as it's assumed the ID is the primary key)
if (rs.next()) {
// assumes 'Top' has a constructor that takes a 'String'
return Optional.of(new Top(rs.getString(1)));
} else {
return Optional.empty();
}
} catch (SQLException ex) {
throw new RuntimeException(ex);
}
}
Your actual implementation may vary, depending on how the rest of your code is designed.

Parametrized queries in java

I am facing a problem with retrieving a column from database
This is my code
public String ShowtimeQur(int MovieID)
{
rs3 = null;
String RoomID=null;
String ShowTime = null;
try
{
String qu ="Select Room_ID from Movie_Shows_in where Movie_ID="+MovieID;
//getRoomQur.setInt(1, MovieID);
rs3=getRoomQur.executeQuery(qu);
RoomID=rs3.getString("Room_ID");
getShowtimequr.setString(1, RoomID);
rs4=getShowtimequr.executeQuery();
ShowTime=rs4.getString("Show_Times");
}
catch (SQLException e)
{
e.printStackTrace();
}
return ShowTime;
}
I keep get this type of error
java.sql.SQLException: Invalid operation at current cursor position.
Use PreparedStatement.
PreparedStatement statement = con.prepareStatement("Select Room_ID from Movie_Shows_in where Movie_ID=?");
statement.setInt(1, MovieID);
ResultSet res = statement.executeQuery()
...
rest of your code
Never, never, never use string concatenation to build queries, as you put yourself at risk of SQL Injection
Like the answer before, you shoud declare a "ResulSet result" variable, and after the execute of the query, you should call "result.next()" method to point the cursor on the first row (initially is pointed to row 0 which does not exist) and then call a retrive data mehod like "result.getString(columnNumber)" by example.

Returning SQL Count function to Java

I'm having problems getting a SQL count query to return the proper value. I know there are already several questions about this topic already posted, but I'm hoping to get a second pair of eyes look over my code to see if they notice anything I've missed. I've found several examples of similar methods that seem to work, and as far as I can see, mine is the same. Here's my method that calls the SQL query:
public int countAddresses(int id) {
ResultSet rs = null;
Statement st = null;
int count = -1;
try{
st = conn.createStatement();
String countAddress = "SELECT COUNT(*) AS AddressCount from address WHERE contact_id =" + id;
rs = st.executeQuery(countAddress);
count = rs.getInt("AddressCount"); //throwing exception
}
catch(SQLException e) {
e.printStackTrace();
}
return count;
}
No matter what tweaks I make, it always returns -1 because the line that reassigns count throws a SQLException. It's worth noting that all my other methods that contact the database are connecting and returning the proper values, and that my java code calls this method after it has successfully called and returned some of these other methods. If anyone can find an error in my code or needs additional info, please let me know. Thanks.
Additional specs:
Spring MVC
XAMPP with a MySQL db
The reason you get an exception is that you are calling getInt without calling next():
rs = st.executeQuery(countAddress);
if (rs.next()) {
count = rs.getInt("AddressCount");
}
The reason it is illegal to call getInt before calling next is that executeQuery() returns you a reader that is not positioned on the first (in this case, the only) result.
Try using
if( rs.next())
count = rs.getInt(1);

Sybase and jdbc- Proc returns select statement - records not able to get

I'm executing a stored procedure from jdbc. The procedure returns the contents of a table 'out_table' which consists of 2 columns key and value, via select query.
I'm using
String query = "{? = call my_proc}";
try {
stmt = conn.prepareCall(query);
stmt.registerOutParameter(1, Types.JAVA_OBJECT);
boolean results = stmt.execute();
while (results) {
ResultSet rs = stmt.getResultSet();
if(rs.next()) {
System.out.println(rs.getString("MSG"));
System.out.println(rs.getInt("SEQ"));
}
}
I'm not able to get any results. I can see a lot of example that returns only a single field of a table from proc and not the whole table. The type 'Types.JAVA_OBJECT', I'm not sure what to use. For me, the output table fields are string and int, respectively.
What am I doing wrong?
Note : my_proc:
CREATE PROCEDURE my_proc
AS
BEGIN
CREATE TABLE #tmp_table
(
MSG VARCHAR(255),
SEQ INT
)
//Insert contents into #tmp_table
..
..
..
SELECT * FROM #tmp_table ORDER BY SEQ
END
go
Your stored procedure doesn't have an out parameter. It produces a ResultSet instead. So you should remove the out parameter definition in your call:
String query = "{call my_proc}";
try (CallableStatement stmt = conn.prepareCall(query)) {
boolean results = stmt.execute();
while (true) {
if (results) {
// results true means: result is ResultSet
try (ResultSet rs = stmt.getResultSet()) {
if(rs.next()) {
System.out.println(rs.getString("MSG"));
System.out.println(rs.getInt("SEQ"));
}
}
} else {
// results false means: result is update count
// or no more results if updateCount is -1
int updateCount = stmt.getUpdateCount();
if (updateCount == -1) break;
// Do something with update count
}
// Move to next result
results = stmt.getMoreResults()
}
}
I added try-with-resource to correctly close resource as soon as you are done with them, I have also added code to correctly handle multiple results including update counts, which especially in Sybase (and SQL Server) might be a complication if your stored procedure doesn't use SET NOCOUNT ON. Your original code also had an infinite loop.
Note that if(rs.next()) might need to be while (rs.next) if there can be more than one result in the table.

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.

Categories