Iterate over result set keys - is it possible? - java

I have a method that takes a database connection, query, and parameters and parses that query into a result set object. This is great but the problem is to get each value out of a result set I have to write one line of code for every row of data I am pulling to then save it in a JSON container. Is there a way to do this systematically so I can automatically parse the data type and create the JSON object based upon the keys fetched from the result set w/o manually specifying the keys?
public static JSONArray q2rs2j(Connection connection, String query, List<String> params) throws Exception {
JSONArray tContainer = new JSONArray();
PreparedStatement pStatement = connection.prepareStatement(query);
int pit = 1;
if(params != null) {
for (String param : params) {
try {
double paramAsDouble = Double.parseDouble(param);
try {
int paramAsInt = Integer.parseInt(param);
pStatement.setInt(pit, paramAsInt);
} catch (NumberFormatException e) {
pStatement.setDouble(pit, paramAsDouble);
}
} catch (NumberFormatException e) {
pStatement.setString(pit, param);
}
pit++;
}
}
ResultSet resultSet = pStatement.executeQuery();
try {
while (resultSet.next()) {
// Iterate through KEYS in the resultSet.next() row
while (hasKey) {
// Store key Name and key Value in variables - todo: determine data type via try parsing as Int, double, etc
String thisKeyName = (nextKeyName);
String thisKeyValue = (nextKeyValue);
JSONObject tObject = new JSONObject();
tObject
.put(nextKeyName, nextKeyValue);
}
tContainer.put(tObject);
}
resultSet.close();
} catch (Exception e) { e.printStackTrace(); }
return tContainer;
}

ResultSetMetaData provides SQL types and java class names.
try (ResultSet resultSet = pStatement.executeQuery()) {
ResultSetMetaData meta = resultSet.getMetaData();
int ncols = meta.getColumnCount();
while (resultSet.next()) {
JSONObject tObject = new JSONObject();
for (int colno = 1; colno <= ncols; ++colno) {
String label = meta.getColumnLabel(colno); // Key
String name = meta.getColumnName(colno);
String sqlType = meta.getColumnType();
String type = meta.getColumnClassName();
String thisKeyName = label;
Object thisKeyValue = result.getObject(colno);
if (sqlType.contains("CHAR")) {
thisKeyVaule = result.getString(colno);
tObject.put(nextKeyName, nextKeyValue);
} else if (sqlType.contains("INT")) {
thisKeyVaule = result.getInt(colno);
tObject.put(nextKeyName, nextKeyValue);
} else {
tObject.put(nextKeyName, nextKeyValue);
}
}
tContainer.put(tObject);
}
}
Using try-with-resources allows automatic closing (useful for Connection, Statement, and ResultSet) - even when on return, break or thrown exception.

Related

Populating a list inside a for loop with an if condition Java

I am trying to populate a list of integers relating to accounts in a database. However, when I populate it and return it, the list only shows one element. I believe it has to do with the ownList.add(owner.getId() being in the scope of the if. When I put that statement outside of the if statement in the for scope the list is null. How should I remedy this to return the full list of account Ids?
Here's my code for that method.
public List<Integer>getAllAccountIDs(List<Account>allAccounts) {
List<Integer>ownList=new ArrayList<>();
allAccounts=accDao.findAll();
try (Connection conn =ConnectionUtil.getConnection()){
String sql = "SELECT id FROM project0.accounts";
Statement stmt =conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
int accId;
allAccounts =accDao.findAll();
while (rs.next()) {
accId=rs.getInt("id");
Account owner=null;
for(int i=0; i <allAccounts.size();i++) {
if(allAccounts.get(i).getId()==accId) {
owner=allAccounts.get(i);
ownList.add(owner.getId());
}
return ownList;
}
}
}catch(SQLException e) {
e.printStackTrace();
System.out.println("NO ACCOUNT EXIST FOR OWNER id");
return null;
}
return null;
}
}
Your statement return ownList is inside for-loop. The function will do what you programmed, that is, return result after the first pass and just after adding the first record.
You might want to move return after for loop (or after while).
The isue is that you return after the first iteration of the nested for loop:
for(int i=0; i <allAccounts.size();i++) {
if(allAccounts.get(i).getId()==accId) {
owner=allAccounts.get(i);
ownList.add(owner.getId());
}
return ownList;
^^^^^^
}
Instead of appending a new item after satisfying the condition for allAccounts.size(), you add an item once and leave the function
You need to modify your function as follows:
public List < Integer > getAllAccountIDs(List < Account > allAccounts) {
List < Integer > ownList = new ArrayList < > ();
allAccounts = accDao.findAll();
try (Connection conn = ConnectionUtil.getConnection()) {
String sql = "SELECT id FROM project0.accounts";
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
int accId;
allAccounts = accDao.findAll();
while (rs.next()) {
accId = rs.getInt("id");
Account owner = null;
for (int i = 0; i < allAccounts.size(); i++) {
if (allAccounts.get(i).getId() == accId) {
owner = allAccounts.get(i);
ownList.add(owner.getId());
}
}
}
return ownList;
} catch (SQLException e) {
e.printStackTrace();
System.out.println("NO ACCOUNT EXIST FOR OWNER id");
}
return null;
}
to make sure that the valid list is returned at the end of try block not after the first iteration of your for-loop.

Take data from a Varray of oracle with java

So I need to to take data from a Varray I created on Oracle with java program. If someone knows. I tried this
My VArray.
CREATE OR REPLACE TYPE cancion IS VARRAY(13) of VARCHAR2(20);/
My function in java.
private ArrayList<String> getCanciones(int codDisco) {
this.open();//open connection
ArrayList<String> x = new ArrayList<String>();
String util;
String sql = "SELECT canciones from TDisco where codDisco = "+codDisco;
try {
PreparedStatement select = con.prepareStatement(sql);
System.out.println(sql);
ResultSet rs = select.executeQuery(sql);
while(rs.next()){
util=rs.getString(1);
x.add(util);
};
} catch (SQLException e) {
e.printStackTrace();
}
this.close();//close connection
return x;
}
Using a simple array doesn´t give problems to take all the data. So I just do it this way and working fine.
private ArrayList<String> getCanciones(int codDisco) {
// TODO Auto-generated method stub
ArrayList<String> x=new ArrayList<String>();
String[] values = null;
ARRAY array;
String sql = "SELECT canciones from TDisco where codDisco = "+codDisco;
try {
PreparedStatement select = con.prepareStatement(sql);
System.out.println(sql);
ResultSet rs = select.executeQuery(sql);
while(rs.next()){
array = (ARRAY) rs.getArray(1);
values = (String[]) array.getArray();
};
} catch (SQLException e) {
e.printStackTrace();
}
for (int i = 0; i < values.length; i++) {
x.add(values[i]);
}
return x;
}

Getting an SQL Exception 'NaN' is not a valid numeric or approximate numeric value

I'm having an SQL exception while i'm trying to parse a JSON object. Here's my code snippet.
public JSONArray paymentMode(String stringObjects) throws SQLException {
JSONObject jsonObject = JSONFactoryUtil.createJSONObject();
//JSONOBJECT RETURN
JSONObject jsonObjectReturn = JSONFactoryUtil.createJSONObject();
JSONObject obj = JSONFactoryUtil.createJSONObject();
JSONArray array = JSONFactoryUtil.createJSONArray();
Session session = null;
Connection conn = null;
CallableStatement callableStatement = null;
try {
// test
BeanLocator beanLocator = PortletBeanLocatorUtil
.getBeanLocator("Mrcos-services-portlet");
BasicDataSource bds = (BasicDataSource) beanLocator
.locate("mrcosDataSourceTarget");
conn = (Connection) bds.getConnection();
jsonObject = JSONFactoryUtil.createJSONObject(stringObjects);
JSONArray jsonArray = jsonObject.getJSONArray("dataArray");
for (int i = 0; i < jsonArray.length(); i++) {
obj = jsonArray.getJSONObject(i);
callableStatement = (CallableStatement) conn
.prepareCall("{call PaymentModeSPv2(?,?,?,?,?,?,?,?,?,?,?,?,?)}");
callableStatement.setString(1, jsonObject.getString("mode"));
callableStatement.setString(2, jsonObject.getString("num1"));
callableStatement.setString(3, jsonObject.getString("date1"));
callableStatement.setString(4, jsonObject.getString("num2"));
callableStatement.setString(5, jsonObject.getString("date2"));
callableStatement.setString(6, jsonObject.getString("remarks"));
callableStatement.setDouble(7, jsonObject.getDouble("amount"));
callableStatement.setString(8, jsonObject.getString("receipt"));
callableStatement.setString(9, jsonObject.getString("algo"));
callableStatement.setString(10, jsonObject.getString("code"));
callableStatement.setString(11, jsonObject.getString("address"));
callableStatement.setString(12, jsonObject.getString("status"));
callableStatement.registerOutParameter(13, Types.INTEGER);
callableStatement.executeQuery();
String xreturn = callableStatement.getString(13);
jsonObjectReturn = JSONFactoryUtil.createJSONObject();
jsonObjectReturn.put("xreturn", xreturn);
array.put(jsonObjectReturn);
System.out.println("jsonObjectReturn : " + jsonObjectReturn);
}
return array;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (callableStatement != null) {
callableStatement.close();
}
if (conn != null) {
conn.close();
}
closeSession(session);
}
jsonObjectReturn = JSONFactoryUtil.createJSONObject();
jsonObjectReturn.put("result", "NO RESULTS FOUND!");
return array;
}
I'm having an error at callableStatement.setDouble(7, jsonObject.getDouble("amount")); saying java.sql.SQLException: 'NaN' is not a valid numeric or approximate numeric value.
The amount is decimal in my database. I also tried to use float instead of double but still gives the same error message. I also tried to pass an integer and a decimal number but still the same error occurs. Please help I'm stuck for a while now. Thank you!
You're getting a javascript [NaN][1] in jsonObject.getDouble("amount"). So you need to deal with it somehow. E.g. you can replace it with 0 or Double.NaN:
String amt = jsonObject.getString("amount");
Double amount = "NaN".equals(amt) ? Double.NaN : Double.parseDouble(amt);
callableStatement.setDouble(7, amount);

Unable to convert the CLOB data into String

I am trying to convert java.sql.Clob data into String by using SubString method (This method giving good performance compared with other). The clob data having near or morethan to 32MB. AS my observation substring method able to to return upto 33554342 bytes only.
if clob data is crossing 33554342 bytes then this it's throwing below sql exception
ORA-24817: Unable to allocate the given chunk for current lob operation
EDIT
CODE:
public static void main(String[] args) throws SQLException {
Main main = new Main();
Connection con = main.getConnection();
if (con == null) {
return;
}
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "SELECT Table_ID,CLOB_FILE FROM TableName WHERE SOMECONDITION ";
String table_Id = null;
String directClobInStr = null;
CLOB clobObj = null;
String clobStr = null;
Object obj= null;
try {
pstmt = con.prepareStatement(sql);
rs = pstmt.executeQuery();
while (rs.next()) {
table_Id = rs.getString( "Table_ID" ) ;
directClobInStr = rs.getString( "clob_FILE" ) ;
obj = rs.getObject( "CLOB_FILE");
clobObj = (CLOB) obj;
System.out.println("Table id " + table_Id);
System.out.println("directClobInStr " + directClobInStr);
clobStr = clobObj.getSubString(1L, (int)clobObj.length() );//33554342
System.out.println("clobDataStr = " + clobStr);
}
}
catch (SQLException e) {
e.printStackTrace();
return;
}
catch (Exception e) {
e.printStackTrace();
return;
}
finally {
try {
rs.close();
pstmt.close();
con.close();
}
catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
NOTE:- here obj = rs.getObject( "CLOB_FILE"); working but I am not expecting this. because I am getting ResultSet object from somewhere as Object. I have to convert and get the data from CLOB
Any Idea how to achieve this?
Instead:
clobStr = clobObj.getSubString(1L, (int)clobObj.length() );
Try something like:
int toread = (int) clobObj.length();
int read = 0;
final int block_size = 8*1024*1024;
StringBuilder str = new StringBuilder(toread);
while (toread > 0) {
int current_block = Math.min(toread, block_size);
str.append(clobObj.getSubString(read+1, current_block));
read += current_block;
toread -= current_block;
}
clobStr = str.toString();
It extracts substrings using a loop (8MB per iteration).
But remember that, as far as I known, Java Strings are limited to 2 GB (this is the reason why read is declared as int instead of long) and Oracle CLOBs are limited to 128 TB.

How to generalize resultset queries?

at the moment I'm working on a script that reads several values from different tables of one database. Every time I start a request, I have to open a statement and create a new resultset which leads to horrible, repetative code. What would be a good way of generalizing this and how can this be done?
Some elements from my code. At the moment there's just one statement and the closing has to be inserted. One of the primary reasons I ask this question.
public static void main(String[] args) throws Exception
{
Connection c = null;
Statement stmt = null;
try
{
//set up database connection
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:/nfs/home/mals/p/pu2002/workspace/Database2");
c.setAutoCommit(false);
stmt = c.createStatement();
//end
//get task id to work with
String Task_id = null;
if(args.length != 0) //if an argument was passed, Task_id will be the first element of the array args (arguments)
{
Task_id = args[0];
}
else if(args.length == 0) //if no arguments were passed, the highest number in the column id from tasks_task will be selected and set as Task_id
{
ResultSet TTask_id = stmt.executeQuery("SELECT max(id) FROM tasks_task");
int t_id = TTask_id.getInt(1);
Task_id = String.valueOf(t_id);
TTask_id.close();
}
//end
//get solution IDs from taks_ids
ArrayList<Integer> List_solIDs = new ArrayList<Integer>(); //create an empty array list
ResultSet SSolution_task_id = stmt.executeQuery("SELECT id FROM solutions_solution WHERE task_id ="+Task_id + " AND final = 1;"); //Sqlite3-Ausdruck SELECT..., Task IDs verändern pro Aufgabe - "SELECT * FROM solutions_solution where task_id ="+Task_id +";"
while (SSolution_task_id.next()) //loops through all elements of SSolution_task_id
{
List_solIDs.add(SSolution_task_id.getInt("id")); //adds all elements of the resultset SSolution_task_id to the list List_solIDs
}
SSolution_task_id.close();
//end
//get logs according to content type
int count = List_solIDs.size();
String log_javaBuilder = null;
List<String> log_JunitChecker = new ArrayList<String>();
for (int i = 0; i < count; i++)
{
boolean sol_id_valid = false;
String solID = String.valueOf(List_solIDs.get(i));
try
{
ResultSet AAttestation_sol_id = stmt.executeQuery("SELECT * FROM attestation_attestation WHERE solution_id =" +solID+";");
int Returned = AAttestation_sol_id.getInt("final_grade_id");
}
catch(Exception e)
{
sol_id_valid = true;
}
if(sol_id_valid ==true)
{
try
{
ResultSet CCresult_javaBuilder = stmt.executeQuery("SELECT log FROM checker_checkerresult WHERE solution_id = " +solID+ " AND content_type_id = 22;"); //"SELECT id FROM checker_checkerresult where solution_id = " +List_solIDs.get(i)+ ";"
log_javaBuilder = CCresult_javaBuilder.getString("log");
CCresult_javaBuilder.close();
ResultSet CCresult_Junit_checker = stmt.executeQuery("SELECT log FROM checker_checkerresult WHERE solution_id = " +solID+ " AND content_type_id = 24;");
while (CCresult_Junit_checker.next())
{
log_JunitChecker.add(CCresult_Junit_checker.getString("log"));
}
CCresult_Junit_checker.close();
}
catch (Exception e)
{
log_JunitChecker.add(null);
}
//end
All types of potential improvements will be welcome.
P.S.: Tried googling.
Seems you want to look at using some ORM layer e.g. http://hibernate.org/orm/
What you're looking for is probably a higher-level layer which
abstracts you from the underlying lower-level JDBC type of coding.
Better than writing generic method by yourself it is always better to use some framework, There are many JPA implementations out there which solve not only this issue but also takes care of multiple persistence layer boiler plate code. Start JPA from Here. You can also use Spring JDBC template as well to solve problem mentioned above Spring JDBC Documentation.
Now, if you really don't want any framework dependency and finish this code quite fast, You can define your own JDBCTemplate class which takes query and parameter map and return ResultSet. This class can handle open connection, query execution and closing connection etc.
What if you try to use generics on methods? this is a quick example, just for illustration, you must improve all this :)
resource: official docs
public static <T> List<T> getSingleValueList(ResultSet rs, Class<T> clazz, String colName) throws Exception {
ArrayList<T> list = new ArrayList<T>();
while (rs.next()) {//loops through all elements of generic list
list.add((T) rs.getObject(colName)); //adds all elements of the resultset rs to the list
}
rs.close();
return list;
}
public static <T> T getSingleValue(ResultSet rs, Class<T> clazz, String colName) throws Exception {
try {
if (rs.next()) {//loops through all elements of generic list
return (T) rs.getObject(colName);
} else {
throw new Exception("no value found.");
}
} finally {
rs.close();
}
}
public static void main(String[] args) throws Exception {
Connection c = null;
Statement stmt = null;
try {
//set up database connection
Class.forName("org.sqlite.JDBC");
c = DriverManager.getConnection("jdbc:sqlite:/nfs/home/mals/p/pu2002/workspace/Database2");
c.setAutoCommit(false);
stmt = c.createStatement();
//end
//get task id to work with
String Task_id = null;
if (args.length != 0) //if an argument was passed, Task_id will be the first element of the array args (arguments)
{
Task_id = args[0];
} else if (args.length == 0) //if no arguments were passed, the highest number in the column id from tasks_task will be selected and set as Task_id
{
ResultSet TTask_id = stmt.executeQuery("SELECT max(id) FROM tasks_task");
int t_id = TTask_id.getInt(1);
Task_id = String.valueOf(t_id);
TTask_id.close();
}
//end
//get solution IDs from taks_ids
ResultSet SSolution_task_id = stmt.executeQuery("SELECT id FROM solutions_solution WHERE task_id =" + Task_id + " AND final = 1;"); //Sqlite3-Ausdruck SELECT..., Task IDs verändern pro Aufgabe - "SELECT * FROM solutions_solution where task_id ="+Task_id +";"
List<Integer> List_solIDs = getSingleValueList(SSolution_task_id, Integer.class, "id"); //create an empty array list
//end
//get logs according to content type
int count = List_solIDs.size();
String log_javaBuilder = null;
List<String> log_JunitChecker = new ArrayList<String>();
List<String> tmplog_JunitChecker;
for (int i = 0; i < count; i++) {
boolean sol_id_valid = false;
String solID = String.valueOf(List_solIDs.get(i));
try {
ResultSet AAttestation_sol_id = stmt.executeQuery("SELECT * FROM attestation_attestation WHERE solution_id =" + solID + ";");
Integer Returned = getSingleValue(AAttestation_sol_id, Integer.class, "final_grade_id");
} catch (Exception e) {
sol_id_valid = true;
}
if (sol_id_valid == true) {
try {
ResultSet CCresult_javaBuilder = stmt.executeQuery("SELECT log FROM checker_checkerresult WHERE solution_id = " + solID + " AND content_type_id = 22;"); //"SELECT id FROM checker_checkerresult where solution_id = " +List_solIDs.get(i)+ ";"
log_javaBuilder = getSingleValue(CCresult_javaBuilder, String.class, "log");
ResultSet CCresult_Junit_checker = stmt.executeQuery("SELECT log FROM checker_checkerresult WHERE solution_id = " + solID + " AND content_type_id = 24;");
tmplog_JunitChecker = getSingleValueList(CCresult_Junit_checker, String.class, "log");
log_JunitChecker.addAll(tmplog_JunitChecker);
} catch (Exception e) {
log_JunitChecker.add(null);
}
//end
}
}
} catch (Exception eeee) {
//handle it
}
}
I hope I gave you a light.
Anyway, frameworks in almost all cases help a lot.

Categories

Resources