In order to retrieve a sample which has an XML field, I use the following code:
String my_call = "CALL my_proc(?)";
PreparedStatement ps = connection.prepareStatement(my_call);
ps.setSQLXML(1, null);
ResultSet rs = ps.executeQuery();
rs.next()
SQLXML desc = rs.getSQLXML(1);
// use desc
And this works perfectly.
But another procedure returns an array of XML. I tried the following code, without success:
String my_call = "CALL my_other_proc(?)";
Array xml = connection.createArrayOf("xml", new Object[]{}); // what's wrong here ?
PreparedStatement ps = connection.prepareStatement(my_call);
ps.setArray(1, xml);
ResultSet rs = ps.executeQuery();
rs.next();
Array xml_array = rs.getArray(1);
SQLXML[] sqlxmls = (SQLXML[])xml_array.getArray(); // what's wrong here ?
I get the following error:
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.sql.SQLXML;
For arrays, custom types other than UUID (via ArrayAssistant) and JSONB (returned as String) is currently not supported.
However, you work around that using String, e.g.:
Array xml_array = rs.getArray(1);
Object[] sqlxmls = (Object[]) xml_array.getArray();
for (Object o : sqlxmls) {
PGobject pgValue = PGobject.class.cast(o);
SQLXML xml = conn.createSQLXML();
xml.setString(pgValue.getValue());
StreamSource source = xml.getSource(StreamSource.class);
// ...
}
For further details, have a look at the code of PgArray.buildArray(...).
This also applies to statement parameters. Instead of using the standard JDBC SQLXML objects, just use a String[] as the argument, e.g.:
List<String> docs = new ArrayList<>();
for (int i = 0; i < 5; i++) {
docs.add("<test index=\"" + (i + 1) + "\"><property value=\"true\"/></test>");
}
Array docsArray = conn.createArrayOf("XML", docs.toArray());
try (PreparedStatement stmt = conn.prepareStatement("INSERT INTO t_array_xml (id, docs) VALUES (?, ?);")) {
stmt.setInt(1, 1);
stmt.setArray(2, docsArray);
stmt.executeUpdate();
}
docsArray.free();
Related
I am trying to execute a stored procedure using SQL Server JDBC in a method:
//Connection connection, String sp_name, Map<String, Object>params input to the method
DatabaseMetaData dbMetaData = connection.getMetaData();
HashMap<String, Integer> paramInfo = new HashMap<String, Integer>();
if (dbMetaData != null)
{
ResultSet rs = dbMetaData.getProcedureColumns (null, null, sp_name.toUpperCase(), "%");
while (rs.next())
paramInfo.put(rs.getString(4), rs.getInt(6));
rs.close();
}
String call = "{ call " + sp_name + " ( ";
for (int i = 0; i < paramInfo.size(); i ++)
call += "?,";
if (paramInfo.size() > 0)
call = call.substring(0, call.length() - 1);
call += " ) }";
CallableStatement st = connection.prepareCall (call);
for (String paramName: paramInfo.keySet()){
int paramType = paramInfo.get(paramName);
System.out.println("paramName="+paramName);
System.out.println("paramTYpe="+paramType);
Object paramVal = params.get(paramName);
st.setInt(paramName, Integer.parseInt(((String)paramVal))); //All stored proc parameters are of type int
}
Let say the stored procedure name is ABC and parameter is #a.
Now DatabaseMetaData returns column name #a but setting st.setInt("#a",0) returns following error:
com.microsoft.sqlserver.jdbc.SQLServerException: Parameter #a was not defined for stored procedure ABC.
Instead, I tried this: st.setInt("a",0) and it executed perfectly.
Now the problem is I have to set the parameters dynamically as I have too many stored procedures and too many parameters but jdbc is giving error.
Edit 1:
As pointed out in one answer that my question is a duplicate of: Named parameters in JDBC, I would like to explain that the issue here is not named parameters or positional ones, rather it is about JDBC not handling the SQL server parameters itself properly or my making some error while invoking it.
Update 2017-10-07: The merge request to fix this issue has been accepted, so this should no longer be a problem with versions 6.3.4 and later.
Yes, it is an unfortunate inconsistency that for mssql-jdbc the parameter names returned by DatabaseMetaData#getProcedureColumns do not match the names accepted by CallableStatement#setInt et. al.. If you consider it to be a bug then you should create an issue on GitHub and perhaps it will be fixed in a future release.
In the meantime, however, you'll just have to work around it. So, instead of code like this ...
ResultSet rs = connection.getMetaData().getProcedureColumns(null, "dbo", "MenuPlanner", null);
while (rs.next()) {
if (rs.getShort("COLUMN_TYPE") == DatabaseMetaData.procedureColumnIn) {
String inParamName = rs.getString("COLUMN_NAME");
System.out.println(inParamName);
}
}
... which produces ...
#person
#food
... you'll need to use code like this ...
boolean isMssqlJdbc = connection.getClass().getName().equals(
"com.microsoft.sqlserver.jdbc.SQLServerConnection");
ResultSet rs = connection.getMetaData().getProcedureColumns(null, "dbo", "MenuPlanner", null);
while (rs.next()) {
if (rs.getShort("COLUMN_TYPE") == DatabaseMetaData.procedureColumnIn) {
String inParamName = rs.getString("COLUMN_NAME");
if (isMssqlJdbc && inParamName.startsWith("#")) {
inParamName = inParamName.substring(1, inParamName.length());
}
System.out.println(inParamName);
}
}
... which produces ...
person
food
Good day, all. I am working on a personal project that needs to interact with an ms access 2016 db. My java application gets data from a user and this info is stored in an Object[]. I am trying to insert the elements of my obj array to a table in my db. This is my code:
Connection conn = null;
PreparedStatement pstmnt = null;
String sql = null;
ResultSetMetaData md = null;
Statement stm = null;
ResultSet rs = null;
int i = 0;
String q = "SELECT * from QueryData";
try{
conn = DriverManager.getConnection("jdbc:ucanaccess://filePath");
stm = conn.createStatement();
rs = stm.executeQuery(q);
md = rs.getMetaData();
int count = md.getColumnCount();
String[] colName = new String[count];
for (int n = 1; n <= count; n++)
colName[n-1] = md.getColumnLabel(n);
while ( i <= data.length) {//data being the object array containing the data to be inserted in db
query = "INSERT into QueryData ('"+colName[i]+"') VALUES ('"+data[i]+"')";
//The following code is where I get the exception
pstmnt = conn.prepareStatement(query);
//some more code follows..
On the first pass throught the while loop, colName[i] is "logDate" which is the first field in the table and data[i] is a LocalDate object formatted as 2016-12-23. I know I did not close the while loop above nor did I given the catch clause, but my program does not run past the pstmnt assignment. I keep getting the exception "net.ucanaccess.jdbc.UcanaccessSQLException: UCAExc:::3.0.7 unexpected token: logDate".
Any assistance will be greatly appreciated as I've scoured the web amd this forum but could not find a working solution to my problem.
You are surrounding your column name with quotes, which isn't allowed. You can use square brackets instead (although not really necessary unless you have spaces in the field names, which Access allows).
query = "INSERT into QueryData (["+colName[i]+"]) VALUES ('"+data[i]+"')";
You might also need to use # instead of ' to delimit the date value. Access used to use # for date delimiters, and I'm not sure if more recent versions accept ':
query = "INSERT into QueryData (["+colName[i]+"]) VALUES (#"+data[i]+"#)";
My task is to get multiple resultsets from a stored procedure & return as json object along with model number, but unfortunately i'm getting only first resultset
to get multiple resultset i'm trying to do something like
boolean isResultExists = false;
isResultExists = callableStatement.execute();
resultSet = callableStatement.getResultSet();
resultSetMetaData = resultSet.getMetaData();
while (resultSet.next()) {
mapObject = new LinkedHashMap<String, Object>();
for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++)
mapObject.put(resultSetMetaData.getColumnLabel(i),
resultSet.getObject(i));
listOfProperties.add(mapObject);
// isResultExists = callableStatement.getMoreResults();
}
this code is returning only 1st resultset & if i try to use
isResultExists = callableStatement.getMoreResults();
I'm getting
java.sql.SQLException: Operation not allowed after ResultSet closed"
Exception
Can anybody suggest best way to get multiple resultset & convert to json Objects
Thanks in advance
I have code
public static String getData(String query) {
String output = "";
try {
String connectionUrl = "jdbc:sqlserver://localhost:1234;databaseName=123;user=123;password=123";
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
con = DriverManager.getConnection(connectionUrl);
String SQL = "select smth from tableName where smth";
stmt = con.createStatement();
rs = stmt.executeQuery(query);
while (rs.next()) {
output = (String) rs.getObject(1);
}
rs.close();
}
catch (Exception e) {
return "ERROR while retrieving data: " + e.getMessage();
}
return output;
}
It works if value is string. But if it integer? Or boolean? How to modify this method so it would be universal, no matter what type data I get I still return it as string?
First retreive the result in ResultSet rs,
then you can write the code like below.
You can check the instance of the object and than assign the value.
String str;
Object obj = (Object)rs.getObject(1);
if(obj instanceof String){
//do you work here for string like below
str=(String)obj;
}
else if (obj instanceof Integer){
//do your work for Integer
}
// same you can do for other types
in this line
output = (String) rs.getObject(1);
if string then use
output = rs.getString(1);
if int
output = rs.getInt(1);
click oracle for more info
You can't accurately do that without using ResultSetMetaData class to get the column type.
Get the column data according to the type of the column.
You are getting the value from the resultset presuming that it is always a String and trying to typecast the Object instance. You should make use of the retrieve methods based on the type. Most of the cases, we will be knowing the datatype of the column values from which we retried the data. You can write the program based on the column's type. that's why ResultSet API has a method for each datatype.
For String
rs.getString(1);
For Int
rs.getInt(1)
Please read the documentation of ResultSet
while (rs.next())
{
String[] data;
data = new String[100];
data[i] = rs.getString("smth");
i = i + 1;
}
Try this you got your data in array.. use array instead of object.
what about toString()?
while (rs.next()) {
output = rs.getObject(1).toString();
}
I need to execute a SQL PreparedStatement in Java using jdbc.
I'm facing problems with one of the parameters because it has SQL content and also Strings from a resource file.
It looks something like this:
Required SQL:
SELECT * FROM Table T WHERE T.value = 10 AND T.display IN ('Sample1', 'Sample2')
In the above query, the Sample1 and Sample2 values must be passed through a parameter to a PreparedStatement.
PreparedStatement:
SELECT * FROM Table T WHERE T.value = 10 ?
In my application code I'm setting the parameters like:
statement.setString(1, "AND T.display IN ('Sample1', 'Sample2')");
However this is not returning the appropriate results.
Is there a better way to build this particular parameter considering it has SQL content and Strings too?
EDIT:
Sample1, Sample2 etc. are strings that are retrieved from an external file at run-time and there can be different number of these strings each time. I.e. there can be only one string Sample1 or multiple strings Sample1, Sample2, Sample3, etc..
EDIT2:
Database being used is Oracle.
The ? placeholder can only be used in a position where a value is expected in the query. Having a ? in any other position (as in your question: WHERE T.value = 10 ?) is simply a syntax error.
In other words: it is not possible to parametrize part of the query itself as you are trying to do; you can only parametrize values. If you need to add a dynamic number of parameters, you will need to construct the query dynamically by adding the required number of parameters and using setString(). For example:
StringBuilder sb = new StringBuilder(
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?");
// Note: intentionally starting at 1, first parameter already above
// Assuming always at least 1 parameter
while (int i = 1; i < params.length; i++) {
sb.append(", ?");
}
sb.append(')');
try (
PreparedStatement pstmt = con.prepareStatement(sb.toString())
) {
for (int i = 0; i < params.length; i++) {
pstmt.setString(i + 1, params[i]);
}
try (
ResultSet rs = pstmt.executeQuery();
) {
// Use resultset
}
}
Use this as PreparedStatement
"SELECT * FROM Table T WHERE T.value = 10 AND T.display IN (?, ?);"
and then call
statement.setString(1, "Sample1");
statement.setString(2, "Sample2");
before executing the statement.
Update:
String generateParamString(int params) {
StringBuilder sb = new StringBuilder("(");
for (int i = 1; i < params; i++) {
sb.append("?, ");
}
sb.append("?)");
return sb.toString();
}
List<String> samples = ... // your list with samples.
String stmtString = "SELECT * FROM Table T WHERE T.value = 10 AND T.display IN "
+ generateParamString(samples.size());
// generate statement with stmtString
for (int i = 0; i < samples.size(); i++) {
statement.setString(i + 1, samples.get(i));
}
// execute statement...