How to prevent a ResultSet from being invalidated on Connection close? - java

I'd like to pass out a result set from a function that executes a query and closes the connection.
But the ResultSet gets invalidated as soon as its parent Connection is closed and throws
java.sql.SQLException: Operation not allowed after ResultSet closed
How to avoid this?

You can't. If you want to get all the data, loop the ResultSet and insert the data into a collection of yours.
Take a look at commons-dbutils - it has a lot of useful helpers.

Also consider using a disconnected result set: CachedRowSet. Quick googling gave me this: Using CachedRowSet to Transfer JDBC Query Results Between Classes.

You want to turn your thinking inside out!
Create a function that will do something with the ResultSet,
and pass it as a closure into the function that runs the query.
def withQuery[T](query:String)(template: ResultSet => T) : T = {
val resultSet = runQuery(query)
val ret = template(resultSet)
resultSet.close
ret
}
val output = withQuery("select * from ...") {
resultSet =>
//some expression that generates a string from the resultset
}
println(output)

That's just not how ResultSet works. It doesn't store the data itself, it just acts as an interface to it.
What you could try instead is having the class that's returning the ResultSets keep a list of the ones it has given out. Each piece of code that uses the ResultSet can call close() on it when it's done with it. Then the first class can check if all ResultSets that have been given out have been closed (using isClosed()), and if so, close the connection.

Related

JDBC: How to clear the contents of a result set before using it again?

I want to clear the contents of a result set (but nothing should happen to the underlying database) before using it again to execute a new query?
Is there any method which will delete its data (but do nothing to the underlying database, that's important), so that I can use the same resultset again to retrieve data from another query?
A quick search in the API documentation gave me the idea that deleting things through a method like deleteRow() will actually delete data from the database as well. I just want to reset/refresh/clear the resultSet, so that when it is again used to get results of another query, it should contain data from that query only, and not the previous one.
Or does it automatically remove its contents itself once we have retrieved them from it (through methods like ResultSet.getInt(), ResultSet.getString() etc.)
EDIT:-
Can we create new result sets for executing each query in the same method?
Lets say you want to execute a query like this :
ResultSet rs = statement.executeQuery("select * from people");
You would iterate of the results with something like this:
while(result.next()) {
// ... get column values from this record
}
Then you just reuse the variable rs to execute another query like:
rs = statement2.executeQuery("select * from cars");
Here is a tutorial on working with ResultSet

Execute multiple queries using a single JDBC Statement object

In JDBC, can I use single Statement object to call executeQuery("") multiple times? Is it safe? Or should I close the statement object after each query, and create new object for executing another query.
E.G:
Connection con;
Statement s;
ResultSet rs;
ResultSet rs2;
try
{
con = getConnection();
// Initially I was creating the Statement object in an
// incorrect way. It was just intended to be a pseudocode.
// But too many answerers misinterpretted it wrongly. Sorry
// for that. I corrected the question now. Following is the
// wrong way, commented out now.
// s = con.prepareStatement();
// Following is the way which is correct and fits for my question.
s = con.createStatement();
try
{
rs = s.executeQuery(".......................");
// process the result set rs
}
finally
{
close(rs);
}
// I know what to do to rs here
// But I am asking, should I close the Statement s here? Or can I use it again for the next query?
try
{
rs2 = s.executeQuery(".......................");
// process the result set rs2
}
finally
{
close(rs2);
}
}
finally
{
close(s);
close(con);
}
Yes you can re-use a Statement(specifically a PreparedStatement) and should do so in general with JDBC. It would be inefficient & bad style if you didn't re-use your statement and immediately created another identical Statement object. As far as closing it, it would be appropriate to close it in a finally block, just as you are in this snippet.
For an example of what you're asking check out this link: jOOq Docs
I am not sure why you are asking. The API design and documentation show it is perfectly fine (and even intended) to reuse a Statement object for multiple execute, executeUpdate and executeQuery calls. If it wouldn't be allowed that would be explicitly documented in the Java doc (and likely the API would be different).
Furthermore the apidoc of Statement says:
All execution methods in the Statement interface implicitly close a statment's [sic] current ResultSet object if an open one exists.
This is an indication that you can use it multiple times.
TL;DR: Yes, you can call execute on single Statement object multiple times, as long as you realize that any previously opened ResultSet will be closed.
Your example incorrectly uses PreparedStatement, and you cannot (or: should not) be able to call any of the execute... methods accepting a String on a PreparedStatement:
SQLException - if [...] the method is called on a PreparedStatement or CallableStatement
But to answer for PreparedStatement as well: the whole purpose of a PreparedStatement is to precompile a statement with parameter placeholders and reuse it for multiple executions with different parameter values.
I can't find anything in the API docs that would state, that you shouldn't call executeQuery() on a given PreparedStatement instance more than once.
However your code does not close the PreparedStatement - a call to executeQuery() would throw a SQLException in that case - but the ResultSet that is returned by executeQuery(). A ResultSet is automatically closed, when you reexecute a PreparedStatement. Depending on your circumstances you should close it, when you don't need it anymore. I would close it, because i think it's bad style not to do so.
UPDATE Upps, I missed your comment between the two try blocks. If you close your PreparedStatement at this point, you shouldn't be able to call executeQuery() again without getting a SQLException.
A Prepared Statement tells the database to remember your query and to be prepared to accept parameterized variables to execute in that query. It's a lot like a stored procedure.
Prepared Statement accomplishes two main things:
It automatically escapes your query variables to help guard against SQL Injection.
It tells the database to remember the query and be ready to take variables.
Number 2 is important because it means the database only has to interpret your query once, and then it has the procedure ready to go. So it improves performance.
You should not close a prepared statement and/or the database connection in between execute calls. Doing so is incredibly in-efficient and it will cause more overhead than using a plain old Statement since you instruct the database each time to create a procedure and remember it. Even if the database is configured for "hot spots" and remembers your query anyways even if you close the PreparedStatement, you still incur network overhead as well as small processing time.
In short, keep the Connection and PreparedStatement open until you are done with them.
Edit: To comment on not returning a ResultSet from the execution, this is fine. executeQuery will return the ResultSet for whatever query just executed.
Firstly I am confused about your code
s = con.prepareStatement();
Is it work well?I can't find such function in JAVA API,at least one parameter is needed.Maybe you want to invoke this function
s = con.createStatement();
I just ran my code to access DB2 for twice with one single Statement instance without close it between two operation.It's work well.I think you can try it yourself too.
String sql = "";
String sql2 = "";
String driver = "com.ibm.db2.jcc.DB2Driver";
String url = "jdbc:db2://ip:port/DBNAME";
String user = "user";
String password = "password";
Class.forName(driver).newInstance();
Connection conn = DriverManager.getConnection(url, user, password);
Statement statement = conn.createStatement();
ResultSet resultSet = statement.executeQuery(sql);
int count = 0;
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number one is: " + count);
count = 0;
resultSet = statement.executeQuery(sql2);
while (resultSet.next()) {
count++;
}
System.out.println("Result row count of query number two is: " + count);

Any way to return a ResultSet in Java?

I found out that Java doesn't let ResultSet to be return as a data type. And would like to ask whether is there any better solution to return results after executing query?
try {
Class.forName("com.mysql.jdbc.Driver");
Connection con = DriverManager.getConnection( "jdbc:mysql://localhost:3306/shopping mall?zeroDateTimeBehavior=convertToNull", "root", "" );
java.sql.Statement stmt = con.createStatement() ;
String query = "SELECT * FROM shops WHERE SHOPNAME LIKE \'%" + shopName + "%\' ORDER BY shopname ASC"; // Select and sort using user input
ResultSet rs = stmt.executeQuery(query) ;
// How to return the result after executing the query?
}
catch ( SQLException err ) {
return null;
}
Don't mind the return null; part. Was testing something. Basically this is my current code for now. How can I return the result after executing query?
You need to provide better context for the error. Is this, for example, a JAX-WS web service endpoint? Anyway, as stated in the trace, your error is a web service error--not a JDBC error. This error can happen for many reasons--usually related to something wrong with the way you are defining the API to your service.
You are certainly allowed to return a ResultSet from a method even if that is a very bad idea, especially from a web service endpoint. A ResultSet can't be serialized into a SOAP message. More generally, to return a ResultSet betrays implementation details to the callers of the method. Why should they know you are using JDBC? Or even that you are talking to a relational (or any) database at all?
The better approach is to populate a model object relevant to your domain with the data in the ResultSet, and that object will be serialized to SOAP via JAXB or whatever you use. Or maybe you just return some text from the database, in which case JAX-WS knows what to do.
Also, make sure you do something with SQLException so you can trace the cause of your actual JDBC errors.
You write a method to retrieve info from a database, where should the data be processed and put into a model class? If you want code that is loosely coupled, then 98% of the time you would process the result set within the same method.
What happens if the query needs to change? You want the changes to be localized into a small of a subset of code as possible.
Take a look at Spring-JDBC JdbcTemplate. This method
List<Map<String, Object>> queryForList(String sql)
returns a List that contains a Map per row. You can use it or make something similar

Java ResultSet working with postgresql

I have a table on my database where it holds 2 columns: uid1 - someone's id, uid2 - his friend.
I want to create a list where I someone's friend - till 5 depth connection.
So I built the following recursive method:
private void recursive(int theUid,ResultSet rs,ArrayList<Integer> friends,int count,int next,PreparedStatement pstmt) throws SQLException{
if(count>=1 && next==theUid){
return;
}
else if(count>=DEPTH_OF_CONNECTION){
return;
}
else{
pstmt.setInt(1,next);
rs=pstmt.executeQuery();
count++;
while(rs.next()) {
friends.add(rs.getInt(1));
recursive(theUid,rs,friends,count,rs.getInt(1),pstmt);
}
}
}
}
And I got the following error:
Exception in thread "main" org.postgresql.util.PSQLException: This
ResultSet is closed. at
org.postgresql.jdbc2.AbstractJdbc2ResultSet.checkClosed(AbstractJdbc2ResultSet.java:2654) at
org.postgresql.jdbc2.AbstractJdbc2ResultSet.next(AbstractJdbc2ResultSet.java:1786)
Can you help me find what is the problem?
Javadoc for jdbc Statement says
By default, only one ResultSet object per Statement object can be open at the same time.
So my guess is you are trying to open too many resultsets for the same PreparedStatement at the same time.
edit:
Postgres jdbc driver doc seems to confirm it:
You can use a single Statement instance as many times as you want. You could create one as soon as you open the connection and use it for the connection's lifetime. But you have to remember that only one ResultSet can exist per Statement or PreparedStatement at a given time.
From what I can tell, you are using the ResultSet object to store your statement results and then passing it as a parameter to the recursive function call. So basically you are overwriting the variable and losing the reference to it. You should either write your sql statement to retrieve the data you need or not pass the same ResultSet object to the recursive call.

Is there any way to prepare a sql statement in Java without using a Connection object?

I'm pretty new to JDBC, so this is probably a very straightforward question.
I have to run several SQL statements, so I'm trying to write a generic "runSQLResultSet" method that takes a String sql statement and returns a ResultSet. I want it to take care of opening the database connection, executing the statement, storing the ResultSet in a CachedRowSetImpl object (so that it will persist after the connection is closed), and closing the connection. I created a method that does this and it works.
My problem now is that I want to be able to use it for dynamic statements that are built with variables. I looked around, and it seems that I should really change my method to take a PreparedStatement instead of just a plain String. Then I can build the PreparedStatement on the other side and pass it to the method. The problem is that I can't seem to create a PreparedStatement without a Connection object. I can open the connection before preparing the statement, but that defeats my purpose of factoring out the database processing into the runSQLResultSet method. I need a way to build a SQL statement with dynamic components, without a connection object, and pass it to a method that will then execute it. Is there any way to do this with a PreparedStatement? Is there any other statement object I can use instead? Otherwise - is there any better way to do this?
public ResultSet excuteStatement(String statement, Object... params){
statement = conn.prepareStatement(statement);
int i = 1;
for (Object o:params){
statement.setObject(i++,o);
}
ResultSet rs = statement.executeQuery();
return rs;
}
You cannot create one without a DB connection. A PreparedStatement will be precompiled in the DB and thus really needs an open connection.
You can instead also just consider to dynamically build the SQL string instead. Generating the PreparedStatement placeholders (the ? things) in a loop and and using String#format() to put them in the SQL string. u can also consider to just pass the variables to your runSQLResultSet method and build there instead.
As per the comments, here's an example:
try {
connection = database.getConnection();
statement = connection.prepareStatement(SQL);
setValues(statement, values);
// ...
.
public static void setValues(PreparedStatement preparedStatement, Object... values) throws SQLException {
for (int i = 0; i < values.length; i++) {
preparedStatement.setObject(i + 1, values[i]);
}
}
If I understand you correctly, you want to use the same query with different variables inserted into the query. You can have your method return the ResultSet like it currently does and pass the variables in as parameters to the method.
then you can put the parameters into the query inside of the method.
public ResultSet getResult(String param1, String param2){
statement = conn.prepareStatement(yourQuery);// conn must be an open connection
statement.setString(1,param1);
statement.setString(2,param2);
ResultSet rs = statement.execusteQuery();
return rs;
}
That's a basic example of how you might do something like that if I understood your question correctly.
what you are missing is the concept of Database connection pooling, you should never be instantiaing connections directly, the pool controls the database connections under the covers. You can look at my open source project SQL Construction Kit for some inspiration on one light weight way to deal with JDBC and how to build dynamic SQL statements using Factory and Builder patterns.

Categories

Resources