I have some duplicated following code
rs = prepStmt.executeQuery();
rs.next();
I want to move it to a method so that I can reuse the code. The method is like:
public static void point(ResultSet rs, PreparedStatement prepStmt){
rs = prepStmt.executeQuery();
result = rs.next();
}
Then I want to get a column value, like, String gender = rs.getString("gender");
And I get the following exception:
java.sql.SQLException: Invalid column name gender. The error does not show up when I did't
encapsulate the logic in the method. So the column name is correct.
Any ideas? Thanks!
You seem to be assuming that the change to the rs parameter will be propagated to the calling code. It won't. Everything in Java is passed by value - including references. Instead, you should probably do something like:
public static ResultSet point(PreparedStatement prepStmt) {
ResultSet rs = prepStmt.executeQuery();
result = rs.next();
return rs;
}
You'd then call it as:
ResultSet rs = point(prepStmt);
(It's not clear what result is either, or whether it's really worth declaring a whole extra method just to avoid calling rs.next() directly... especially when it means the result of rs.next() is somewhat obscured...)
Related
I'm working on a little project and right now I have a problem. I need to search in my database all movies which have same genre. I wrote this function for this thing, but doesn't work so well. In principle I want for each result found to create a new object named Movie and to return him. I tested my function but I have two movies with same gerne and he return me only one object. And my question is why doesn't return me all objects? He should to return all my objects.
public Movie extraction(String Genre)throws SQLException{
Statement stmt=con.createStatement();
ResultSet rs=stmt.executeQuery("select * from movies where genre='"+Genre+"'");
while(rs.next()){
String name=rs.getString("name");
String genre=rs.getString("genre");
int year=rs.getInt("year");
int metascore=rs.getInt("metascore");
System.out.println(name);
return new Movie(name,genre,year,metascore);
}
return null;
}
The problem here is that you return within the loop instead of adding the results to a List and returning that list when the loop completes.
There are several other issues with the code which are not related to your problem but may create problems in the future:
You create a statement and a resultset and never close them.
You are using string concatenation to generate the query instead of a PreparedStatement. If the string Genere is received from some untrusted user (for example in a web application) that user may use SQL injection to transform the query into whatever he wants.
A better solution (warning: untested) is something like the following:
List<Movie> movies = new ArrayList<>();
try(PreparedStatement stmt= con.prepareStament("select * from movies where genre=?")) {
stmt.setString(1, Genere);
try(ResultSet rs = stmt.executeQuery()) {
while(rs.next()){
String name=rs.getString("name");
String genre=rs.getString("genre");
int year=rs.getInt("year");
int metascore=rs.getInt("metascore");
movies.add(new Movie(name,genre,year,metascore));
}
}
}
return movies;
the try statement (called "try with resources") ensures that the statement and the resultset .close methods are called when the block ends.
I have two methods in my class, First I am calling method dbExecuteStatement(), which execute the sql query. After execution of sql query, I get a ResultSet object. I am saving this ResultSet object in a static hashMap, so that on my next method call fetchResults(), I can use the existing result set to retrieve the results. Reason for saving the ResultSet object in a map is ,in fetchResults() method request parameter, I will get the max fetch row size, and on basis of that value I will be iterating the result set. Both of this methods are supposed to be called individual from the client side.
Now the problem, I am facing is that, When I am iterating the ResultSet object in fetchResults() method, I am getting the row count zero. If I fetch the same ResultSet from a hashMap in dbExecuteStatement(), I get the actual row count i.e 5 in my case. I checked the ResultSet object that I have put in the hash map in fetchResults() method and dbExecuteStatement(), it is the same object. But If get the ResultSetMetaData object in fetchResults() method and dbExecuteStatement(), they are coming different. Can someone help me in understanding the cause, Why I am getting the result count zero.
Below is the code:
public class HiveDao1 {
private static Map<Object,Map<Object,Object>> databaseConnectionDetails
= new HashMap<Object,Map<Object,Object>>();
//This method will execute the sql query and will save the ResultSet obj in a hashmap for later use
public void dbExecuteStatement(DbExecuteStatementReq dbExecuteStatementReq){
//I already have a connection object saved in map
String uniqueIdForConnectionObject = dbExecuteStatementReq.getDbUniqueConnectionHandlerId();
Map<Object,Object> dbObject = databaseConnectionDetails.get(uniqueIdForConnectionObject);
Connection connection = (Connection) dbObject.get(DatabaseConstants.CONNECTION);
try {
Statement stmt = connection.createStatement() ;
// Execute the query
ResultSet resultSet = stmt.executeQuery(dbExecuteStatementReq.getStatement().trim()) ;
//save the result set for further use, Result set will be used in fetchResult() call
dbObject.put(DatabaseConstants.RESULTSET, resultSet);
/*
* Now below is the debugging code,which I put to compare the result set
* iteration dbExecuteStatement() and fetchResults method
*/
ResultSet rs = (ResultSet) dbObject.get(DatabaseConstants.RESULTSET);
ResultSetMetaData md = (ResultSetMetaData) dbObject.get(DatabaseConstants.RESULTSETMETADATA);
System.out.println("==ResultSet fethced in dbExecuteStatement=="+rs);
System.out.println("==ResultSet metadata fetched in dbExecuteStatement ==="+rs.getMetaData());
int count = 0;
while (rs.next()) {
++count;
}
if (count == 0) {
System.out.println("No records found");
}
System.out.println("No of rows found from result set in dbExecuteStatement is "+count);
} catch (SQLException e) {
e.printStackTrace();
}
}
/*
* This method fetch the result set object from hashMap
* and iterate it on the basis of fetch size received in req parameter
*/
public void fetchResults(FetchResultsReq fetchResultsReq){
String uniqueIdForConnectionObject = fetchResultsReq.getDbUniqueConnectionHandlerId();
Map<Object,Object> dbObject = databaseConnectionDetails.get(uniqueIdForConnectionObject);
try {
//Fetch the ResultSet object that was saved by dbExecuteStatement()
ResultSet rs = (ResultSet) dbObject.get(DatabaseConstants.RESULTSET);
ResultSetMetaData md = (ResultSetMetaData) dbObject.get(DatabaseConstants.RESULTSETMETADATA);
System.out.println("ResultSet fethced in fetchResults at server side dao layer======"+rs);
System.out.println("ResultSet metadata fetched in fetchResults at server side dao layer======"+md);
int count = 0;
while (rs.next()) {
++count;
}
if (count == 0) {
System.out.println("No records found");
}
//Here the row count is not same as row count in dbExecuteStatement()
System.out.println("No of rows found from result set in fetchResults is "+count);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Expanding on my comment (And #Glenn's):
Using a ResultSet more than once
When you write debug code that iterates a ResultSet, the cursor moves to the end of the results. Of course, if you then call the same object and use next(), it will still be at the end, so you won't get any more records.
If you really need to read from the same ResultSet more than once, you need to execute the query such that it returns a scrollable ResultSet. You do this when you create the statement:
Statement stmt = connection.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_READ_ONLY );
The default statement created by connection.createStatement() without parameters returns a result set of type ResultSet.TYPE_FORWARD_ONLY, and that ResultSet object can only be read once.
If your result set type is scroll insensitive or scroll sensitive, you can use a statement like rs.first() to reset the cursor and then you can fetch the records again.
Keeping the statement in scope
#Glenn's comment is extremely important. The way your program works right now, it may work fine throughout the testing phase, and then suddenly in production, you'll sometimes have zero records in your ResultSet, and the error will be reproducible only occasionally - a debug nightmare.
If the Statement object that produces the ResultSet is closed, the ResultSet itself is also closed. Since you are not closing your Statement object yourself, this will be done when the Statement object is finalized.
The stmt variable is local, and it's the only reference to that Statement that we know of. Therefore, it will be claimed by the garbage collector. However, objects that have a finalizer are relegated to a finalization queue, and there is no way of knowing when the finalizer will be called, and no way to control it. Once it happens, the ResultSet becomes closed out of your control.
So be sure to keep a reference to the statement object alongside your ResultSet. And make sure you close it properly yourself once you are done with the ResultSet and will not be using it anymore. And after you close it remember to remove the reference you have kept - both for the statement and the result set - to avoid memory leaks. Closing is important, and relying on finalizers is a bad strategy. If you don't close it yourself, you might run out of cursors at some point in your database (depending on the DBMS and its configuration).
Im new in java and SQL, Im repeating a problem that i don't know how to avoid it:
assume i want to make two executeQuery, one inside the other in the getRequestsFromDB method i make the first executeQuery and in the second method isProfessionalHasThatProfession i make the second executeQuery:
private Vector<ClientRequest> getRequestsFromDB() throws SQLException {
Vector<ClientRequest> retVal = new Vector<ClientRequest>();
ResultSet result = null;
try {
for (int i=0 ; i<_userBean.getProfession().length ; ++i ){
result = _statement.executeQuery("SELECT * FROM "+_dbName+"."+CLIENTS_REQUEST_TABLE+" WHERE "+CLIENTS_REQUEST_T_PROFESSION+"='"+_userBean.getProfession()[i]+"'");
while(result.next()){ //HERE IN THE SECOND LOOP GETTING NULL EXCEPTION
if(isProfessionalHasThatProfession(result.getString(CLIENTS_REQUEST_T_PROFESSION))){
retVal.add(cr);
ClientRequest cr = new ClientRequest
(result.getString(CLIENTS_REQUEST_T_CLIENT_ID),
result.getString(CLIENTS_REQUEST_T_CITY),
result.getString(CLIENTS_REQUEST_T_DATE),
result.getString(CLIENTS_REQUEST_T_PROFESSION));
}
}
}
} catch (SQLException ex) {
throw ex;
}
return retVal;
}
the second function:
private boolean isProfessionalHasThatProfession(String profession) throws SQLException {
ResultSet result = null;
try {
result = _statement.executeQuery("SELECT "+WORKER_PROFESSIONS_T_PROFESSION+" FROM "+_dbName+"."+WORKER_PROFESSIONS_TABLE+" WHERE "+WORKER_PROFESSIONS_T_PROFESSIONAL_ID+"='"+_userBean.getProId()+"'");
while(result.next()){
if(result.getString(1).equals(profession)){
return true;
}
}
} catch (SQLException ex) {
throw ex;
}
return false;
}
in the second loop im getting a SQLException: "operation not allowed after ResultSet closed", i have tried:
close in finally the result with result.close() but also i get exception null pointer exception.
i'm really don't know how to deal with that, ideas?
Thank You!
youre reusing _statement (which i assume is global?) to get 2 different ResultSets, but then you return to the 1st ResultSet (in the outside function) after you got the 2nd (inside the inner function, which automatically closed the 1st) - try using 2 separate statements
Check this link :http://download.oracle.com/javase/1.4.2/docs/api/java/sql/Statement.html
By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if the reading of one ResultSet object is interleaved with the reading of another, each must have been generated by different Statement objects. All execution methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists.
And you are reusing your statement
See this quote, from the ResultSet API:
A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
Looks like you are using a class, or global, scope Statement (_statement), which backs both the resultset you are trying to iterate over, and query details of some sort for each entry in the resultset in your isProfessionalHasThatProfession method. but when you execute a new query with the same Statement, your old ResultSet is closed.
So, you'll need a separate statement for the second query.
From the Java API:
By default, only one ResultSet object per Statement object can be open
at the same time. Therefore, if the reading of one ResultSet object is
interleaved with the reading of another, each must have been generated
by different Statement objects.
So you have to generate a new Statement for the second ResultSet. Please don't forget to close the Statements respectively.
I am implementing a code that uses JDBC driver.
Below is the code that I made.
public class MysqlUtils {
public Connection conn;
public ResultSet rs;
public PreparedStatement stmt;
public MysqlUtils(String address, String id, String passwd) {
try {
conn = DriverManager.getConnection(address, id, passwd);
stmt = null;
rs = null;
} catch (SQLException e) {
// error management
}
}
public void getSomeData(long id) {
try {
stmt = conn.prepareStatement("SELECT * FROM some_table");
rs = stmt.executeQuery();
rs.next();
System.out.println(rs.getString("some_column");
} catch (SQLException e) {
// error management
}
}
}
I have declared Connection conn, ResultSet rs, PreparedStatement stmt as member variables because somehow I thought that might help me enhance performance.
I have a couple of questions.
If I call getSomeData() consecutively, will stmt and rs be assigned new objects every time?
Regardless of the answer to the question above, if I run this code in a multi-threaded environment(multiple threads using MysqlUtils class), will there be a mix-up because I didn't declare ResultSet rs in getSomeData()?
Was declaring Connection conn, ResultSet rs, PreparedStatement stmt as member variables a bad choice? In other words, is my implementation of JDBC a viable one?
Thanks for the help.
Yes. The method will be executed, and thus stmt and rs will take new values. Of course, you might have multiple instances of your class, and thus multiple instances of those two fields.
Yes. This code is completele thread-*un*safe. Public fields in general should almost always be avoided. Especially in a multi-threaded environment
Yes, it's a bad choice. The scope of a variable should be as small as possible. And these variables are used in a single method, and reassigned every time. They should be local variable.
Also:
a method getSomeData() should return something, and not just printing something
the ResultSet and the Statement should be closed, in a finally block
I hope the error management doesn't consist in swallowing the exception
I would advise using spring-jdbc, which takes care of all the plumbing code for you, and avoids all the problems your code currently has.
Do not use ResultSet outside of the method.... with while(rs.next) (rs=resultSet) you are looping through a database table and retrieving values!
I am using a SELECT statement to get data from a table and then insert it into another table. However the line "stmt.executeQuery(query);" is inserting the first line from the table then exits. When I comment this line out, the while loop loops through all the lines printing them out. The stacktrace isn't showing any errors. Why is this happening?
try{
String query = "SELECT * FROM "+schema_name+"."+table;
rs = stmt.executeQuery(query);
while (rs.next()) {
String bundle = rs.getString("BUNDLE");
String project_cd = rs.getString("PROJECT_CD");
String dropper = rs.getString("DROPPER");
String week = rs.getString("WEEK");
String drop_dt = rs.getString("DROP_DT").replace(" 00:00:00.0","");
query = "INSERT INTO INDUCTION_INFO (BUNDLE, PROJECT_CD, DROPPER, WEEK, DROP_DT) "
+ "VALUES ("
+ bundle+","
+ "'"+project_cd+"',"
+ dropper+","
+ week+","
+ "to_date('"+drop_dt+"','YYYY-MM-DD'))";
System.out.println(query);
stmt.executeQuery(query);
}
}catch(Exception e){
e.printStackTrace();
}
You are re-using the Statement that was used to produce rs on the last line of your loop.
This will close the ResultSet rs. As stated in the documentation:
A ResultSet object is automatically closed when the Statement object that generated it is closed, re-executed, or used to retrieve the next result from a sequence of multiple results.
You need to use a second Statement object to execute the INSERT statements.
Statement objects can only do one thing at a time, so when you execute that INSERT, you invalidate the ResultSet which it generated. You'll need to create a second Statement object to perform the INSERT.
From the Statement documentation: "By default, only one ResultSet object per Statement object can be open at the same time. Therefore, if the reading of one ResultSet object is interleaved with the reading of another, each must have been generated by different Statement objects. All execution methods in the Statement interface implicitly close a statment's current ResultSet object if an open one exists."
if you use the same statement, it will invalidate the previous result set. You should use a different statement to perform updates/inserts.
This is from the Java docs of interface Statement:
By default, only one ResultSet object per Statement object can be open
at the same time.
So you better use a second Statement or even better a PreparedStatement.
And to execute an INSERT SQL statement you should use executeUpdate() instead of executeQuery().