Best practices for reusing prepared statements while keeping the code clean? - java

TL;DR: What's the recommended approach for reusing PreparedStatement objects in Java, without making a mess out of the code?
If I want to reuse them I have to define them all early in the code, and it becomes a mess.
If I don't create them until I need to, I can keep the code tidy and clean, but then I can't reuse the objects.
I have a method like this:
PreparedStatement psQuery = conn.prepareStatement("select ...");
PreparedStatement psChild = conn.prepareStatement("select ... where parent = ? and ...");
ResultSet rsQuery = psQuery.executeQuery();
while (rsQuery.next()) {
psChild.setInt(1, rsQuery.getInt("id"));
psChild.executeQuery();
...
}
I create two preparedStatement first, and then reuse them every time I need to execute those specific SQL queries. I don't define psChild inside my loop because then I'd be creating a new prepared statement in each iteration, instead of just reusing it.
Now, my code is much more complex. I'm actually using 13 different preparedStatement instances, and the code spreads through a few hundred lines. I'd very much like to split it into different methods, but I'm not sure how to properly do it. I can think of two options. The first one is like I'm doing it right now, only splitting into methods:
PreparedStatement psQuery = conn.prepareStatement("select ...");
PreparedStatement psChild = conn.prepareStatement("select ... where parent = ? and ...");
ResultSet rsQuery = psQuery.executeQuery();
while (rsQuery.next()) {
processChildren(rsQuery.getInt("id"), psChild);
}
The problem is, I end up with a processChildren with this signature:
private static void processChild(
...,
final PreparedStatement psFoo,
final PreparedStatement psBar,
final PreparedStatement psDoc,
final PreparedStatement psGrumpy,
final PreparedStatement psHappy,
final PreparedStatement psSleepy,
final PreparedStatement psDopey,
final PreparedStatement psBashful,
final PreparedStatement psSneezy,
final PreparedStatement psYetAnotherOne,
final PreparedStatement psAndAnotherOne,
final PreparedStatement psLastOne,
...) {
Not exactly great.
The other option would be to create each prepared statement in the method where I'll need it. That would be much cleaner, but it's the same as creating them inside the loop: I wouldn't be reusing them.
There is yet another option, to declare the variables as class attributes, this way I could create them first, and then reuse without the need to clutter the "children method" signatures. But this feels even more wrong, in the same way that using a global variable would. Worse, 13 "global" variables all of them with the same class and very similar names. No way I'm doing that!
How could I proceed?
Note: I'm aware of much better persistence solutions, such as JPA. I'm not looking for an alternative to prepared statements, I only want to know what's the usual approach in cases like mine.
Edit: It seems like I oversimplified my example. This is closer to what I need to do:
Retrieve all the records from database 1.
For each one of them (first loop):
Check if it exists in another database 2.
If it doesn't, create it in database 2, and:
Retrieve all children from database 1.
For each of the children (second loop):
Check if child exists in database 2.
If it doesn't, then insert it.
So I have two levels of nested loops which I can't get rid of. And creating the prepared statements over and over inside of the loops seems like a poor idea.

Related

PreparedStatement parameter index out of range

I am getting a parameter index out of range error when executing a prepared statement. I have several other statements working correctly. The only difference with this query is it's the only UPDATE. The rest are all INSERT, ADD, DELETE etc. Any guidance on what I may be doing wrong would be greatly appreciated.
sqlStatement = "UPDATE customer SET customerName = ?, addressId = ? WHERE customerId = ?;";
StatementHandler.setPreparedStatement(ConnectionHandler.connection, sqlStatement);
StatementHandler.getPreparedStatement().setString(1, name);
StatementHandler.getPreparedStatement().setInt(2, AddressDAO.getAddressId(address));
StatementHandler.getPreparedStatement().setInt(3, customerId);
StatementHandler.getPreparedStatement().executeUpdate();
Error:
java.sql.SQLException: Parameter index out of range (3 > number of parameters, which is 1).
I have put a couple print statement sin the middle of the code block and it seems to fail on the 3rd parameter. All values coming in are valid and match the types being assigned. MySQL is being used and the statement works fine if executed in the console.
Thank you for reading and any help you can provide.
Edit: Here is the statement handler method I am using as well. I am combing through to see what else I should add to help get this thing figured out. Thank you for the comments!
public class StatementHandler {
/**
* create statement reference
*/
private static PreparedStatement preparedStatement;
/**
* method to create statement object
*/
public static void setPreparedStatement(Connection connection, String sqlStatement) throws SQLException {
preparedStatement = connection.prepareStatement(sqlStatement);
}
/**
* getter to return statement object
*/
public static PreparedStatement getPreparedStatement(){
return preparedStatement;
}
}
Your snippet doesn't make it clear, but I can guess. I'll list a series of conclusions I'm drawing; you'd have to doublecheck these:
StatementHandler is a class (not a variable). (reason: You've capitalized it).
setPreparedStatement and getPreparedStatement are static methods in the StatementHandler class. (follows naturally).
You are using multiple threads (reason: That would be sufficient to explain this problem).
You aren't synchronizing (reason: Same as #3).
Then this result is obvious: You can't do that. Your entire VM has one global 'prepared statement' with multiple threads calling setPreparedStatement and getPreparedStatement in more or less arbitrary orders. One thread calls setPreparedStatement, then another thread does, then the first tries to get the prepared statement the other one set, and it all goes to hades in a handbasket.
You can't do it this way. Heck, you can't even share a connection between two threads (as they'd be getting in each other's way and messing up your transactions).
If you don't quite get what static does (And it is, admittedly, a bit of an advanced topic), don't ever use it. You can pretty much write all the java you'd ever want without using static methods. The one exception is public static void main which must be static, but just make that the one-liner: new MyClass().go();, with go() being a non-static method, and you're good to go.
I'd like to go one step further than rzwitserloot and presume that your AddressDAO uses StatementHandler, too.
The query for AddressDAO.getAddressId(address) has probably one parameter, which matches the 1 from the Exception, and replaces the prepredStatemt before setting the third parameter.
As proof it would be suffient assigning AddressDAO.getAddressId(address) to a variable(and use it afterwards) before setting the prepared statement.
Alternativly you can get once the prepared statement in a variable and use this variable afterwards.

Is it worth to create a PreparedStatement for int values?

I create PreparedStatement when I need to pass arguments to the answers there do not address your problem, please edit to explain in detail the parts of your question that are unique.
Title
Is it worth to create a PreparedStatement for int values?
SQL queries, but is it worth to prepare a statement to pass int arguments and to be closed after the execution?
void delete(int key, int orElse) throws SQLException
{
try(PreparedStatement pst = this.connection.prepareStatement(
"DELETE FROM a_table WHERE the_int_primary_key=? OR random_int_field=?"
))
{
pst.setInt(1, key);
pst.setInt(2, orElse);
pst.executeUpdate();
}
}
Is it worth to prepare that statement? Is it going to increase the security in anyway?
What if I do that with a normal statement? Is it risky in any way? Will it execute a bit faster?
void delete(int key, int orElse) throws SQLException
{
try(Statement stm = this.connection.createStatement())
{
stm.executeUpdate(
"DELETE FROM a_table WHERE the_int_primary_key="+key+" OR random_int_field="+orElse
);
}
}
Edit:
This question is not duplicated of Do prepared statements slow down program conspicuously? because:
The other question plains to reuse the prepared statement multiple times, I plan to use it only once, the documentation already specifies that it's faster to reuse PreparedStatements
I'm planning to use the statement only for ints and I'm worried about SQL Injections but at the same time I'm not sure if it's possible to inject SQL with primitive int parameters, the micro speed enhancement would be just a small plus, I'm not asking just because of performance. The other question only wants to speed it up and may be using strings, dates, or other non-primitive types.
From java docs:
A SQL statement is precompiled and stored in a PreparedStatement
object. This object can then be used to efficiently execute this
statement multiple times.
To answer your question: Yes very worth it, it's important to use a prepared statment, it's the best way to protect you against injection attacks(like sql injection), a normal statement will do nothing to protect against these types of attacks, even if you make your very own "good" sql parser it will presumably fail to protect against some attacks.
Ask instead: "Is is more complicated to use prepared statement?". Using nothing but plain JDBC, it's a tiny bit longer. So there's about nothing to gain. So don't take any risk (SQL injection) and don't mix plain (unprepared) statements in.
In case you find it too verbose, then look for a library providing better syntax or maybe write yourself a utility allowing things like
try (MyPreparedStatement pst = new MyPreparedStatement(connection,
"DELETE FROM a_table WHERE the_int_primary_key=? OR random_int_field=?"
))
{
pst.executeUpdate(1, 2);
}

Is there a way I can extract table data from a SQL database without using the ResultSet class in Java?

I am programming a fairly simple database application in Java. The typical way of extracting data from a SQL database goes something like this:
private Connection conn = null;
private Statement stmt = null;
private ResultSet rs = null;
conn = DriverManager.getConnection("jdbc:mysql://localhost/test?" + "user=monty&password=greatsqldb");
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT foo FROM bar");
rs.first();
int firstColumn = getInt(1);
String secondColumnn = getString(2);
My problem is that ResultSet lacks a lot of functionality that I want (for instance a getRow() method that would return the whole row or a getNumberOfRows() method that would tell me how many rows are in the ResultSet). I could code these myself, but I have a feeling that someone else already has.
Does anyone know of an class that provides these methods and other useful functionality?
No offense to anyone who loves the ResultSet class.
If you wish to accomplish your task only with plain vanilla JDBC, then I would say no.
Data access can be classified into two categories.
Connected architecture
Your ResultSet falls under this.
Disconnected architecture
CachedRowSet falls under this.
The above answer might not be directly related to your query but this will be helpful for your understanding. You can also think of using ORM(hibernate,JPA,MyBatis) for more Object-oriented approach of data access functionality. It has many advantage over JDBC.
Hope this will be helpful
Cheers!
You could look into an appropriate ORM such as hibernate or JPA. These will allow you to interact with the DB through objects and their OO links (compositions, lists, sets, etc).
http://www.hibernate.org/
http://en.wikipedia.org/wiki/Java_Persistence_API

ResultSet.update(..) vs st.executeUpdate(sql)

Is it better to use ResultSet.update method or directly st.executeUpdate(sql) in order to update a database? Which one is better than the other and why?
Are these 2 methods totally interchangeable?
Thanks in advance.
Resultset.update requires a special kind of ResultSet, created like this:
Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet rs = stmt.executeQuery(...);
Not all database support updateable result sets of this kind. The second option, however, may be safely used on any RDBMS as it's widely supported.

SqlInjection with prepared statement without bind variable?

As we know the best way to avoid sql injection is using prepared statement with bind variables. But i have question what
if i use just prepared statement but not bind variables like below where customer id is coming from User interface
String query ="select * from customer where customerId="+customerId;
PreparedStatement stmt = con.prepareStatement(query); //line1
Does line 1 take care restricting sql injection even when i have not used bind variables?
I agree the best way is below but if above approach also takes care of restrcting sql injection then i would prefer above one(as
its a legacy project)
String query ="select * from customer where customerId=?";
PreparedStatement stmt = con.prepareStatement(query);
stmt.setInt(1, 100);
Is prepared statement without using bind variable sufficient to make sure sql injection not possible?
One have to distinguish several matters.
Using prepared statement won't do any help just by itself.
As well as there is no harm in using non-prepared way in general.
The thing works only when you need to insert dynamical part into query.
So, in this latter case such a dynamical part have to go into query via placeholder only, which actual value have to be bound later (placeholder is a ? or any other mark that represents the actual data in the query).
The very term "prepared statement" implies using placeholders for all the dynamical data that goes into query. So,
if you have no dynamical parts in the query, there would be obviously no injection at all, even without using prepared statements.
if you're using a prepared statement, but inject values directly into query instead of binding them - it would be wide open to injection.
So, again - only with placeholders for all dynamical data prepared statement would work. And it works because:
every dynamical value have to be properly formatted
prepared statement makes proper formatting (or handling) inevitable.
prepared statement does proper formatting (or handling) in the only proper place - right before query execution, not somewhere else, so, our safety won't rely on such unreliable sources like
some 'magic' feature which rather would spoil the data than make it safe.
good will of one (or several) programmers, who can decide to format (or not to format) our variable somewhere in the program flow. That's the point of great importance.
prepared statement affects the very value that is going into query, but not the source variable, which remains intact and can be used in the further code (to be sent via email or shown on-screen).
prepared statement can make application code dramatically shorter, doing all the formatting behind the scenes (*only if driver permits).
Line 1 will not check if develeper want or not want to drop table. If you write query it's assumed it is Ok.
Goal of sql injection is to prepare values that allows making additional sql query without will nor knowledge of developer. Quering your website with fake values in attributes.
Example:
id = "'); DROP ALL TABLES; --";
query = "select * from customer where customerId="+id;
PreparedStatement ensures that special symbols (like ' or ") added to query using setInt/setString/etc will not interfere with sql query.
I know this is an older post, I just wanted to add that you avoid injection attacks if you can make sure you are only allowing integers into your query for line 1. String inputs are where the injection attacks happen. In the sample above, it is unclear which class of variable 'customerId' is, although it looks like an int. Since the question is tagged as Java, you can't do an injection attack with an int, so you should be fine.
If it is a string in line 1, you need to be confident that the 'customerId' comes from a secure source from which it must be an integer. If it comes from a post form or other user generated field then you can either try to escape it or convert it to an integer to be sure. If it is a string, cast it to an integer and you will not need to bind params.

Categories

Resources