For example if I have:
connection.prepareStatement("SELECT * FROM users
WHERE status=? AND otherAttribute=?");
Where in some cases I might want a specific status and in others I want all statuses. How can I basically do something like:
preparedStatement.setInt(1, mySpecificRequestedStatus);
and in other cases:
preparedStatement.setInt(1, allStatuses); // wildcard
So that I don't have to have multiple PreparedStatements, especially for more complex WHERE clauses in the SELECT statements where the permutations can be higher.
You can do it like this:
connection.prepareStatement("SELECT * FROM users
WHERE (status=? OR ?=1) AND otherAttribute=?");
The second ? is for the "all statuses" attribute: set it to 1 if you want to ignore the status; set it to zero if you would like the specific status to be considered.
You don't have a choice, you have to pick one or the other as I originally suspected and asked.
You can either:
Create a complex prepared statement with if clauses
Create a complex SQL statement which doesn't have the best performance
There really isn't a good solution to this. This is why ORM's exist, to abstract this out. The problem is that ORM introduce other issues/complexities in exchange for simplifying other things.
Stephane Grenier,
Why not create a function or procedure in database and call function or procedure from java? This would solve many hassles, would keep your java code neat and of course no hard coding of sql queries.
Regards
Related
I know that comparing with a normal Select, putting the values directly into the SQL statements, like this way:
Statement command = connection.createStatement( "SELECT * FROM person WHERE name = "+ nameVar);
Doing that with JDBC and positional parameters, like this:
String statment= "SELECT * from Users WHERE name=? and pass =?";
sentence = conexion.prepareStatement(consulta);
sentence.setString(1, nameVar);
sentence.setString(2, passVar);
Is better for several things, like avoid SQL injection, and also uses less memory (for caching the execution plan only once) and performance (for not doing the same execution plan again and again)
But if you have a lots of "?" may be difficult to correlate the parameter with the variable, and that can cause an error.
My question is, if there is a difference between doing the positional parameters as above with doing "named parameters" like this:
String statment= "SELECT * from Users WHERE name=:nameParam and pass =:passParam";
sentence = conexion.prepareStatement(consulta);
sentence.setString("nameParam", nameVar);
sentence.setString("passParam", passVar);
Because is easier and can skip errors
UPDATE
by the comments seems that only using JPA/Hibernate can use :parameters. Well The question remains. There is any difference using Hibernate?
As you point out you are building a PreparedStatement so that the DB can cache it's execution plan etc. So that is where all the performance gain comes from when the driver actually talks to the DB.
The other benefits are for the code. It is DRY-er (Do not Repeat Yourself) you can prevent SQL injection attacks as you can validate the params and they can't add "structural changes" to the SQL. You can keep a ref to the PreparedStatement Object so no need to re-create it etc etc.
But you are doing this on both cases, so the main benefits are the same. Under the covers an Array of params is passed to the Driver to run the prepared statement.
Positional params will most likely just index into the Array (depending on your Drivers' implementation) whereas named params maintain a map of name to position and generate the correct array when required.
Given that the number of params in a SQL statement tends to be small (can usually count them on one or two hands) creating the array is very fast, and negligible compared to the cost of running the query over the network to the DB.
So IMHO use whichever helps you reason about your code best. The performance difference is minuscule.
I have written universal DAO layer for mySQL (it can save\get any class object that extends Entity to\from table using Reflection and ResultSetMetaData). My implementation of it has little concatenation in sql query. Is it waste all advantages of prepared statement or I just loose little perfomance to concat String and nothing more?
For example piece of code for entity deletion:
PreparedStatement prepStatement = con.prepareStatement("DELETE FROM "
+ tableName + " WHERE id = ?");
prepStatement.setLong(1, id);
The main benefit of PreparedStatements is when you have code that behaves in a similar way to this pseudocode:
PreparedStatement ps = con.prepareStatement("blabalblabla");
for (int i = 0; i < a gazillion times; i++) {
// Set parameters into ps
...
// execute already prepared statement
ps.execute();
}
That is, you prepare once and execute many times, each time with different sets of parameters. This allows the driver / database to perform potentially costly operations (such as parsing) only once and then reuse that work. Apart from that, using PreparedStatement may be interpreted as a hint to the driver that it should cache that statement resources or something because it is going to be used later, but I don't think it will have as much impact as the "prepare once execute many" approach.
Your use of concatenation to add the table names won't disable the optimizations that your JDBC driver does (if any). But anyway, if your code does more of "prepare once execute once" than it does "prepare once execute many", then PreparedStatement might only have a minor performance benefit.
Note that all of the above is highly database / driver dependent. For example, Oracle performs a lot better if you use PreparedStatements in the way I have described as "prepare once execute many". And as a last advice, don't forget that you should avoid concatenating parameter values unless you have no other option, for both performance AND SECURITY reasons.
It's recommended to use the prepared statements for the DB performance improvement.
In theory the DB drivers cache the prepared statements (you might require to enable the caching on connection object).
I would assume that concatenation is not as critical.
Keep in mind that tableName might be case sensitive in the driver cache.
I would review your DB driver features, and you should be able to debug the driver, and monitor the database to see how your statements are handled/executed.
The variable tableName in your example may introduce vulnerability for SQL injection but it may be alternative ways to protect against this. For instance,
Map<String,String> myTables; // key and value are the same.
tableName = myTables.get(tableName); // safe known value or null.
Generally, it is better just to use prepared statements consistently to stay away out of trouble. However sometimes building query (most often where query) "on the fly" can save many lines otherwise close to duplicate code so it is difficult to say "never do this".
I already used the search here (and other forums as well) but haven't found an answer exacty to what I'm trying to do.
I know that it can easily be done in some other way, and this is just a small sandbox-framework I'm coding for a University course... in a real environment I'd just take Spring, Hibernate etc.
So what I did was coding myself a small generic Data Access Layer with POJOs, working with generic methods to retrieve, check or insert data to the database (Oracle). Most of this is done through PreparedStatements.
This is working as long as I don't have joins... is it possible to put in a Column as parameter?
Example:
Table A has Attribute X + others
Table B has Attribute Y + others
PreparedStatement with query SELECT * FROM A,B WHERE "A"."X" = ?
And then fill in "B"."Y" as the parameter...
The database doesn't throw me an error or exception, but the ResultSet returned after executing the statement is empty. Is it just not possible to do, or am I just missing some escaping?
I'm using PreparedStatement.setString(int index, String value) to fill in the parameter... in lack of ideas which other setX method I could use...
Again, in a real project I'd never code that myself, but rather use something like Spring or Hibernate and not re-invent the wheel, but I see it as an interesting exercise to code such a generic small data access layer myself.
No, JDBC does not allow this. Only column values can be set. If you want to make dynamic changes to the sql statement you will have to do it before you create the PreparedStatement.
I just found that there are places in our code that use Prepared Statement even though we always deal with inserting one row to the table.
I'm wondering if using Prepared Statement when only inserting one row has some overhead that worth modifying this code to use Statement.
When you use PreparedStement, not just your query execution is faster there are other advantages too.
You queries execute fast as PreparedStatement results into the query being precompiled on the database and reused.
PreparedStatement, your queries are dynamic. Meaning, you define the query only once, and reuse the same again with different parameters. String concatenation also achieves it but its crude way doing this. Quoting this link
The important thing to remember is to never construct SQL statements
using string concatenation of unchecked input values. Creating of
dynamic queries via the java.sql.Statement class leads to SQL
Injection.
When you use PreparedStatement you prevent the SQL injection attacks. In Prepared statement, you do not use string concatenation for adding the runtime parameters but instead set the parameter explicitly in the compiled query and the parameters passed are escaped automatically by JDBC Driver for PreparedStatement.
On the security side, Prepared Statements are used especially to prevent SQL Injection attacks. About efficiency, it very much depends on the nature of the statement you are dealing with. You may also find interesting this other answer:
Why PreparedStatement is preferable over Statement
A PreparedStatement is prefrable to a simple Statement as it offes your some security against SQL injection.
In a PreparedStatement every parameter is checked for its type and automatically escaped. This means that inserting String with an ' is save with PreparedStatement whereas you have to escape special characters yourself when not using a PreparedStatement.
Also you cannot insert some String where a Number is expected.
If even your code inserts only one row it may be called many times in which case PreparedStatement is supposed to be faster. As for Statement it also needs to be compiled before execution so it's hardly be any faster
Is there a difference and what is better practice to use: String.format() and manually insert values or PreparedStatement and parse values to placeholders (what is more size of code)?
As a general rule, never ever use plain string formatting where PreparedStatement could be used. That latter has knowledge of your database SQL syntax. And will takes care of "shielding" every special characters much better than you.
Failure to follow that rule will result in high risks of SQL Injection in your code.
See Does the preparedStatement avoid SQL injection?
There is a fundamental difference: a PreparedStatement will make whatever it takes so that the values you feed to it (via the .set*() methods) come as "neutral" to the database (or, rather, the "JDBC engine").
Note that PreparedStatement is an interface. As such, when using a JDBC driver for this or that database engine, it will be able to act differently depending upon said engine.
Do not use String.format() for that. Its role is quite different! String.format() cannot prevent SQL injection attacks; PreparedStatement can, unless its implementor did a really, really bad job.