Reuse of ResultSet member variable in a multi-threaded environment - java

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!

Related

Java JDBC select records

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.

how to merge two different resultsets from two different DBs in java

I have two resultsets, one from DB2 and one from Sybase.
I want to merge these resultsets based on some condition,for which i have made one function which will take two resultsets and merge them.
But i am getting SQL exception- Resultset Closed
I am using Connection object and Prepared statement to connect to the respective DB and execute the query.
public void ExecuteDB2Query(SQLQuery){
Connection DB2con = DB2Sess.connection();
PreparedStatement statement = DB2con.prepareStatement(SQLQuery);
MyResulset1= statement.executeQuery();
}
Another method:
public void ExecuteSybaseQuery(SQLQuery){
Connection Sybasecon = SybaseSess.connection();
PreparedStatement statement = Sybasecon.prepareStatement(SQLQuery);
MyResulset2= statement.executeQuery();
}
Final merge method
puble void merge{
while(MyResultset1.next()){
while(MyResultset2.next()){
<some code here>
}
Do we have any way by which i can loop through these two result sets? without any exception.
It looks like you are trying to loop through those two result sets with a nested loop. That can't be done, since after the inner loop finished its first iteration, MyResultset2 cannot be used anymore.
I suggest you iterate over the two result sets separately and store their data in some Collections. Then you can iterate over those Collections however you like.
Ok, i'll give you your code check it.
public void ExecuteDB2Query(SQLQuery){
Connection DB2con = DB2Sess.connection();
PreparedStatement statement = DB2con.prepareStatement(SQLQuery,ResultSet.TYPE_SCROLL_INSENSITIVE);
myResulset1 = statement.executeQuery();
}
public void ExecuteSybaseQuery(SQLQuery){
Connection Sybasecon = SybaseSess.connection();
PreparedStatement statement = Sybasecon.prepareStatement(SQLQuery,ResultSet.TYPE_SCROLL_INSENSITIVE);
myResulset2 = statement.executeQuery();
}
public void merge{
while(myResultset1.next()){
myResultset2.first();
while(myResultset2.next()){
<some code here>
}
}
}
make changes in this code according to your requirement.

Move rs to method causes Exception

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...)

Best practices: working with DB in Java

First of all, I'm new to Java.
I'm trying to figure out what would be a good/handy way to work with DB from Java. I'm using c3p0 for connection pooling. Hibernate or other ORM is not an option this time, we decided to stick with "plain SQL" for now.
Currently basic retrieval of data looks like this:
private int getUserID(int sessionID, String userIP) {
int result = 0;
Connection conn = null;
PreparedStatement st = null;
ResultSet rs = null;
try {
// Application.cpds is an instance of c3p0's ComboPooledDataSource
conn = Application.cpds.getConnection();
st = conn.prepareStatement("SELECT user_id, user_ip, is_timed_out FROM g_user.user_session WHERE id = ?");
st.setInt(1, sessionID);
rs = st.executeQuery();
if ( rs.next() ) {
if ( !rs.getBoolean("is_timed_out") && userIP.equals(rs.getString("user_ip")) ) {
result = rs.getInt("user_id");
}
}
}
catch (SQLException e) {
e.printStackTrace();
}
finally {
if ( rs != null ) {
try { rs.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if ( st != null ) {
try { st.close(); } catch (SQLException e) { e.printStackTrace(); }
}
if ( conn != null ) {
try { conn.close(); } catch (SQLException e) { e.printStackTrace(); }
}
}
return result;
}
The code looks very long for such a basic operation. Another problem is that most of the code would have to be repeated in many places (declaring Connection, PreparedStatement, ResultSet, closing them, catching exceptions). Though, this is what I see in most examples when googling.
In PHP I would create a wrapper class that would have method select() that accepts 2 arguments (string)sqlQuery and (array)parameters and would return simple array of data. Wrapper class would also have few more specific methods, like:
selectValue() for single value (e.g., select count(*) from user)
selectRow() for single row (e.g., select name, surname from user where id = :user_id)
selectColumn for single column (e.g., select distinct remote_address from user)
Is anything like this practiced in Java? Or is there anything better / handier? Or should I use same style as in getUserID() example above? As I said, ORM is not an option this time.
Thanks in advance :)
edit: Currently DBConnection class is written. It gets connection from c3p0 connection pool in constructor. It has few public methods for working with DB: select() for tabular data, selectValue() for single value, selectRow() and selectColumn() for single row or column, as well as insert(), update(), delete() and ddl(). Methods accept String query, Object[] params arguments, with params being optional. insert(), update() and delete() return Integer which is result of PreparedStatement.executeUpdate(). select methods return different results:
ArrayCollection<HashMap<String, Object>> select()
Object selectValue()
HashMap<String, Object> selectRow()
ArrayCollection<Object> selectColumn()
The last problem is with compiler warnings - "warning: [unchecked] unchecked cast". This is because all methods call single private method that returns Object and cast its result to mentioned types. As I am new to Java, I'm also not sure if I have chosen appropriate types for selects. Other than that, everything seems to work as expected.
If the an ORM is no option, you could still use Spring's JDBC helper classes:
http://docs.spring.io/spring-framework/docs/4.1.0.RELEASE/spring-framework-reference/html/jdbc.html
Or you could simply write some helper methods on your own. Maybe a DBUtil.close(conn, st, rs); would be nice.
And by the way, you really should use a logging framework instead of "e.printStackTrace()"
EDIT:
One more thing: I think it's kind of hard to add ORM afterwards, when you have all the SQL already written in plain JDBC. You can't refactor that stuff, you have to throw it away and do it again.
EDIT:
You don't have to close the resultSet if you are closing the statement anyway. The Java ResultSet API reads:
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.
Beside that, C3P0 does resource management as well and and closes Statements when you return a connection. You might to look that up too.
To avoid the repetition of code and perhaps makes things simpler.
What you could do is create a Database class.
In the class you could then create general purpose methods for access to the database.
For eg.
if the class is called DBManager.java then inside
create methods
private connect()
public boolean update()
public ResultSet query()
Reason for connect method is obvious, you use it get your connection. Since its private you call it in the constructor of DBManager.
You then use your update() method to allow you to perform SQL inserts,update,delete and the like, basically any SQL operation that doesn't return any data except for maybe a status of its success is done with the update method.
Your query method is used when you want to do a select query. You can thn return the resultset and then iterate through the results in the calling method/class
How you handle exceptions is up to you. It may be nicer on you to handle exceptions in the DBManager class that way you won't have to handle them in the various classes that you make a query from.
So instead of
public ResultSet query() Throws SQLException{
you would use a try catch inside the query method like you did in your examples above.
The obvious advantage of handling it in the dbmanager class is that you won't have to worry about it in all the other classes that make use of your sql connection.
Hope that's helpful
in response to your comment:
Its up to you what you return, the ResultSet being return is only an idea but maybe it'd be best to return a collection of some sort instead of an array, maybe? depending on what you need. The resultset needn't be closed.
public ResultSet query(String strSql) {
try {
Statement tmpStatement = connection.createStatement();
ResultSet resultSet = tmpStatement.executeQuery(strSql);
return resultSet;
} catch (java.sql.SQLException ex) {
//handle exception here
return null;
}
}
your update can then look like so
public boolean updateSql(String strSQL) {
try {
Statement tmpStatement = connection.createStatement();
tmpStatement.executeUpdate(strSQL);
return true;
} catch (java.sql.SQLException ex) {
//handle exception
return false;
}
}
erm, you can then use your query method like so
ResultSet r = query(sql);
try {
while (r.next()) {
someVar[i] = r.getString("columnName");
}
} catch (SomeException ex) {
//handle exception etc
}
But then again as you said instead of returning a result set you could change the query method to copy your results to an array or collection and then return the collection and close the statement with
tmpStatement.close();
But when a Statement object is closed, its current ResultSet object, if one exists, is also closed.(from api docs)
Its good practice to free up database resources as soon as so copying your result to a collection object and then closing your statement is probably best. Again its up to you.
" Hibernate or other ORM is not an option this time, we decided to stick with "plain SQL" for now."
Out of curiosity, what was the reason to stick with plain SQL? Looking at the example and question you mentioned first obvious answer would be use ORM and don't bother - in most cases standard ORM feature list would be sufficient.
Obviously there are plenty of reasons not to use ORM's, so I'm interested in yours?
I think the level o granularity is always a developer decision. I mean, the great thing of having so many exceptions and validations is that you can capture the specific error and act according to it, however if what you need doesn't require that level of robustness and as you are showing it is just to print out the stack trace, I think a wrapper method can be useful in your case.

How can I have a Java method return multiple values?

I was just wondering whether there's a way to make a Java method return multiple values.
I'm creating an application that uses the jdbc library to work with a database. I can successfully enter values into the database but I need a way to return them, and this is where I'm a bit stuck. I creating a form into which the user enters a specific value (an ID number) which is then passed to by Database class which carries out my database work.
Database newQuery = new Database();
newQuery.getCust(c_ID); //uses the GetCust method in my class,
//passing it the ID of the customer.
The getCust() method in my Database class creates the following query:
ResultSet Customer = stat.executeQuery("SELECT * FROM Persons WHERE Cust_ID=C_ID");
I need a way to return the results that are stored in Customer back. Any ideas?
Why not just return Customer, or create a small class with all the values you want returned in it and return that class?
You can't exactly return multiple values from a method in Java, but you can always return a container object that holds several values. In your case, the easiest thing to do would be to return the ResultSet, Customer.
If you're concerned about exposing your data layer to your UI, you can copy the data from the ResultSet into a structure that is less specific to the database, either a List of Maps, or perhaps a List of Customer objects, where Custom is a new class that represents your business entity.
So your actual problem is that you didn't know how to set values/parameters in a SQL query? The only right way to do this is using PreparedStatement.
String sql = "select * from Customers where Cust_ID = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setLong(custId);
resultSet = preparedStatement.executeQuery();
It not only eases setting Java objects (String, Long, Integer, Date, InputStream and so on) in a SQL query, but most importantingly it will save you from SQL Injection risks. Further it's also faster than a Statement because it's precompiled.
As to your code logic, you should always close the DB resources in the reverse order in the finally block to avoid resource leaks in case of exceptions. Here's a basic example how to obtain a Customer the right JDBC way:
public Customer find(Long customerId) throws SQLException {
String sql = "SELECT id, name, age FROM customer WHERE id = ?";
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Customer customer = null;
try {
connection = getConnectionSomehow();
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setLong(custId);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
customer = new Customer();
customer.setId(resultSet.getLong("id"));
customer.setName(resultSet.getString("name"));
customer.setAge(resultSet.getInteger("age"));
}
} finally {
if (resultSet != null) try { resultSet.close(); } catch (SQLException ignore) {}
if (preparedStatement != null) try { preparedStatement.close(); } catch (SQLException ignore) {}
if (connection != null) try { connection.close(); } catch (SQLException ignore) {}
}
return customer;
}
You may find this tutorial useful to get more insights and examples.
Each customer could be accompanied with a little interface that describes a method that takes multiple arguments. You then pass in the object that implements this interface to have the result delivered to it.
The object that implement it can of course be the same as the method calling getCustomer belongs to, so it just passes a reference to 'this' and assign the arguments to fields that you can expect to have been set when it all returns.
Consider using an object/relational mapping library. It will handle the details of packaging the multiple data values you need to return from the JDBC ResultSet into a single Java bean object.
Which one to pick is another discussion. A lot of smart people use Hibernate. The Java platform includes JPA. Using one off the shelf will save you from inventing your own, which is what devising your own combination of objects and collections would end up being.
In addition to using Hibernate - take a look at Spring. It supports connection pooling etc and allows you to abstract the JDBC away from your code completely.
It will either return you a List of Maps or a List of your custom type (depending on how you call it).

Categories

Resources