I have some JDBC code as follows where I am using gemfireXD as DB.
I am trying to parallelize the execution of a procedure on a list of inputs. The size of the list I am passing as the parameter to the procedure is not fixed and it is determined after the execution of the query in the code below. This list can be too big in size.
Code:
private static void executeProc (Connection cxn) throws SQLException {
Statement stmt = null;
try {
stmt = cxn.createStatement();
stmt.execute("select distinct field1 from Table1");
ResultSet rs = stmt.getResultSet();
List<String> fieldList = new ArrayList<String> ();
while (rs.next()) {
fieldList.add(rs.getString("field1"));
}
Array fieldArray = cxn.createArrayOf("VARCHAR", fieldList.toArray(new String[fieldList.size()]));
CallableStatement callableStmt = cxn.prepareCall("{CALL procedure1() ON TABLE Table1 WHERE field1 IN (?)}");
callableStmt.setArray(1, fieldArray);
callableStmt.execute();
} catch (SQLException e) {
e.printStackTrace();
}
}
Error:
while executing the code it is giving below runtime error.
Feature not Implemented : createArrayOf(String, Object[])
Question:
The gemfireXD documentation says Data Aware procedure can be executed passing a list using setArray method. But when I am working on actual API, the setAaary() method is expecting an Array Object instead of a List.
GemfireXD document gives an example of such procedure call in its documentation (1.4.0) which supports setArray() method. The direct example from the documentation is as follows: -
// GemFire XD data-aware procedure invocation.
CallableStatement callableStmt = connection.prepareCall("{CALL order_credit_check() ON TABLE Orders WHERE customerID IN (?)}");
callableStmt.setArray(1, <-list of customer IDs->);
How do I call preparedStatement.setArray to set the parameter in the query?
Could any one please suggest any other implementation approch to achieve the desired result?
GemFire XD does not support Array data type so createArrayOf method will not work.
For the kind of query in you question where you want to use in-list in the where clause, you may have to set each individual value as a parameter
CallableStatement callableStmt = cxn.prepareCall("{CALL procedure1() ON TABLE Table1 WHERE field1 IN (?, ?, ?)}");
callableStmt.setInt(1, 1);
callableStmt.setInt(2, 2);
callableStmt.setInt(3, 3);
....
Related
Just looking for some small help here. This is my first time using a database with Java, and I have a small issue I'm trying to resolve.
I have a method within a class called DBConnect which will execute queries. I'd like to insert this List into my database.
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
DBConnect.executeQuery();
I suppose I'd start creating the query string with
String insert = ("INSERT INTO Client_Data (card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) VALUES
The above fields being the columns I'm trying to insert into, and Client_Data being my table.
How do I go about formatting the fields in my list to query properly?
After Values I believe the format is ('data','data','data').
Could anybody experienced with JDBC please assist me?
Thank you.
I would use PreparedStatements to insert the values into your table.
/*
* Code
* I am assuming that you have a Connection object named conn.
* This is just a simple example
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
/*
* The question marks are placeholders for the values you will insert.
*/
ps.setString(1, "abc");
ps.setInt(2, 123);
ps.setDouble(3, 3.1416);
ps.execute(); // The insert is executed here
} catch(SQLException e) {
// Your exception handling code
}
If you need to insert values into your table using a loop, you may also execute the inserts as a batch:
/*
* Code
*/
try(
PreparedStatement ps = conn.prepareStatement(
"insert into yourTable(field1, field2, field3) values (?,?,?)"
) {
for(int i = 0; i < 10; i++) {
ps.setString(1, "abc");
ps.setInt(2, 123 * i);
ps.setDouble(3, 3.1416);
ps.addBatch(); // The insert is added to a batch, pending for execution
}
ps.executeBatch(); // All the inserts added to the batch are executed.
} catch(SQLException e) {
// Your exception handling code
}
Reference:
The Java Tutorials: JDBC - Using Prepared Statements (You may also want to read the full JDBC tutorial)
Java API reference for the PreparedStatement class
Basically, you should be trying to use PreparedStatement, there are a number of very good reasons for this, but in your case, it's the simplest way to bind the values from your List to the Statement
For example, you could start by defining the insert statement as a constant, this isn't required, but for the example, it made it easier...
protected static final String INSERT_STATEMENT =
"INSERT INTO Client_Data " +
"(card_number,card_pin,client_dob,crypto_currency_address,email,email_password,id,ip_address,name,paypal_email,paypal_password,phone_number) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)";
Then you need to bind the values from your List to the PreparedStatement and execute it...
List<String> data = new ArrayList();
data.add(name);
data.add(bank);
data.add(pin);
data.add(email);
data.add(pass);
data.add(phone);
data.add(paypal_email);
data.add(paypal_pass);
data.add(IPV4Assistant.getExternalIPAddress());
data.add(crypto);
data.add("1");
data.add(dob);
// Replace with your own connection management, just here for
// example reasons
try (Connection con = DriverManager.getConnection(url)) {
try (PreparedStatement stmt = con.prepareStatement(INSERT_STATEMENT)) {
for (int index = 0; index < data.size(); index++) {
stmt.setObject(index + 1, data.get(index));
int rows = stmt.executeUpdate();
// Check the value of rows if you want to know how
// many rows were affected by the change
}
}
} catch (SQLException exp) {
// Possibly throw this to the call instead...
exp.printStackTrace();
}
I assume, you'll be passing the List as an parameter to some method.
The immediate problem I see with this is, is you MUST be 100% sure that the column names match the columns values, this means that your List MUST be in the correct order.
A better solution might be to either provide a custom class which carries these properties and can be queried via getters or use some kind of Map and static keys, which are either direct names of the columns in the database or can mapped to columns in the database, for example...
public static final String CLIENT_NAME = "name";
//... Other column names/keys...
//...
Map<String, Object> clientData = new HashMap<String, Object>();
clientData.put(CLIENT_NAME, name);
//...
stmt.setObject(CLIENT_NAME, clientData.get(CLIENT_NAME));
You should also avoid inserting String into columns which have different data type requirements (such as Date, TimeStamp and/or numbers). Instead, you should be trying to use the correct JDBC mapping types where possible
Take a look at Using Prepared Statements for more details
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.
I am having the hardest time calling an Oracle stored procedure from a java runtime environment. The stored procedure that I am calling has 2 parameters 1 in and 1 out. Here is how I call the stored procedure... How do you get the resultSet from an Oracle ref_cursor
ds = (DataSource)initialContext.lookup("JDBC/EPCD13DB");
conn = ds.getConnection();
callableStatement = conn.prepareCall(storedProcCall);
callableStatement.setString(1, input1);
callableStatement.registerOutParameter(2, OracleTypes.CURSOR);
callableStatement.execute();//(ResultSet) callableStatement.getObject(1);
ResultSet rs = callableStatement.getResultSet();
while(rs.next()){
Provider tempProv = new Provider();
tempProv.setResourceId(rs.getLong("res_id"));
tempProv.setFirstName(rs.getString("First_Name"));
tempProv.setLastName(rs.getString("Last_Name"));
tempProv.setMiddleName(rs.getString("Middle_Name"));
ObjList.add(tempProv);
}
rs.close();
You should be able to retrieve the ResultSet with:
ResultSet rSet = (ResultSet)callableStatement.getObject(2);
Does this help you? Seems like you have to call getObject and cast it into a result set before querying on the result set.
Credit:: http://www.mkyong.com/jdbc/jdbc-callablestatement-stored-procedure-cursor-example/
I believe it returns only one output(oracle cursor)
ResultSet rs=(ResultSet) callableStatement.getObject(2);
and then iterate your cursor result set for records inside:
while(rs.next()){
Provider tempProv = new Provider();
tempProv.setResourceId(rs.getLong("res_id"));
tempProv.setFirstName(rs.getString("First_Name"));
tempProv.setLastName(rs.getString("Last_Name"));
tempProv.setMiddleName(rs.getString("Middle_Name"));
ObjList.add(tempProv);
}
In spring framework fetching database cursor results can be easily achieved. It has inbuilt classes like maprow, storedprocedure to serve the purpose. PFB the link
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/jdbc.html#jdbc-simple-jdbc-call-1
Using MySQL 5.5, STS 2.9.2, mysql-connector-java-5.1.21-bin.jar
I want to get an array from ResultSet.
So I coded like this:
try {
Connection conn = DriverManager.getConnection(url, id, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);
if (rs.next()) {
Array code = rs.getArray("code");
Array count = rs.getArray("count");
Object objCode = code.getArray();
Object objCount = count.getArray();
int[] itemCode = (int[]) objCode;
int[] itemCount = (int[]) objCount;
// do something
}
conn.close();
stmt.close();
rs.close();
} catch(SQLException e) {
printError(e);
}
Then, I got a SQLFeatureNotSupportedException at getArray().
If I delete the line, it does not give that exception.
I googled about it, and I found that that`s because JDBC driver does not support this method. I cannot understand what it says, and how do I solve this problem?
.getArray method fetches ARRAY SQL data type. Like that: .getDecimal fetches DECIMAL SQL data type and so on... MySQL (for example) does not support ARRAY data types. So, Java reports: feature not supported.
If you want to fetch all fields as an indexed array, use .getString(1) for a VARCHAR column value at index 1.
Example query: SELECT id,title FROM news WHERE id = 1;
So you can fetch id with .getInt(1), and title with .getString(2).
That's all I can say about that ))
getArray("columnName") or getArray("columnIndex") methods are not supported by MySql Database server.
I'm writing some JDBC code which calls a Oracle 11g PL/SQL procdedure which has a Custom Object return type. I can get the code to call the procedure, but how do I access the returned Custom Object to obtain it's contained values?. An example of my code calling the procedure is below:
PLSQL Code:
Procedure GetDataSummary (p_my_key IN KEYS.MY_KEY%TYPE,
p_recordset OUT data_summary_tab,
p_status OUT VARCHAR2);
Java Code:
String query = "begin manageroleviewdata.getdatasummary(?, ?, ?); end;");
CallableStatement stmt = conn.prepareCall(query);
// Single IN parameter
stmt.setInt(1, 83);
// Two OUT parameters, one a Custom Object, the other a VARCHAR
stmt.registerOutParameter(2, OracleTypes.ARRAY, "DATA_SUMMARY_TAB");
stmt.registerOutParameter(3, OracleTypes.VARCHAR);
stmt.execute(stmt);
How do I get the result back fron this?
We've cracked it:
oracle.sql.ARRAY result2 = (oracle.sql.ARRAY) stmt.getObject(2);
ResultSet rs = result2.getResultSet();
oracle.sql.STRUCT elements = (oracle.sql.STRUCT) rs.getObject(2);
String result = null;
if (elements != null) {
Object[] objs = elements.getAttributes();
result = objs[2];
}
System.out.println("Result: " + result);
In our case this prints the third element in our Custom Object type.