using java variable in sql statement - java

I'm trying to build a web page to better learn Java and SQL. My question is, is there a way in Java to make a generic SQL select statement? For example:
SELECT var1 FROM var2 WHERE var3=var4
or something of the sort.
My idea is to fill the vars with user selected items from the web page. I know this can be done in PHP using the Post method, but I'm not using PHP. Also, I've read about the Prepared Statement in Java, but seems only to work when the used after the comparison operator; ex:
SELECT * FROM table Where attr = ? &
Also, I do know i can do the hard coded version of "SELECT " + var1 + "FROM " + var2 + "WHERE attr = " + var3 + " " but that doesn't seem very generic and prone to a lot of errors.
Incase: I'm trying to build this test page using HTML & JSP.

What you are doing with the ? is parameterizing the query. The query can only be parameterized for values not names of tables or columns.
Every time you run a query. The database has to create a query plan. If you are running the same query again and again, you can reduce this overhead by creating a PreparedStatement.
The first execution of PreparedStatement will generate the query plan. The subsequent executions will reuse the same plan.
Same query here means, it is identical in all respects except values used in where clause, expressions etc.
If you change the Column or Table name or modify the structure of the query, then it is a different query and will require a different query plan. A PreparedStement is not useful in this case and you should stick to the hardcoded version you talked about. Because of this reason you will get an error if you try to parameterize Table or Column names in PreparedStement.
Having said that. It is not advisable to take such a generic approach for queries. If your queries are that simple, you can benefit from ORM tools. You would not have to maintain even a line of SQL. For complex queries you have an option of using ORM specific query language or JPQL or Native SQL. Look for JPA + Hibernate

Your specific usage is not permitted by JDBC. You need to hard code the table name when creating the prepared statement. If you really do want to do that I suggest you use String concatenation to create the SQL statements and then create a PreparedStatement with parameters to handle the where part. In case you are wondering why bother with PreparedStatements in the specific solution, it's to avoid SQL injection.

You can use PreparedStatement to achive your objective.
For example -
String query = "SELECT * FROM table Where attr = ?";
PreparedStatement pt = con.prepareStatement(query);
pt.setString(1, attribete);
pt.executeUpdate();

There is no such direct provision in any of SQL packaged classes or others to replace table, column names along with query parameter values, in a query string, using a single method.
You require to depend on both PreparedStatement and any of String methods replace(...) and replaceFirst(...) to achieve your requirement.
String sql = "Select $1, $2 from $3 where $4=? and $5=?";
sql = sql.replaceFirst( "$1", "col1_name" );
sql = sql.replaceFirst( "$2", "col2_name" );
sql = sql.replaceFirst( "$3", "table_name" );
sql = sql.replaceFirst( "$4", "col4_name" );
sql = sql.replaceFirst( "$5", "col5_name" );
// .. and so on
PreparedStatement pst = con.prepareStatement( sql );
// use relevant set methods to set the query parametrs.
pst.setXXX( 1, value_for_first_query_parameter ); // from a variable or literal
pst.setXXX( 2, value_for_second_query_parameter); // from a variable or literal
// ... and so on

If you are using JDBC, can try this
PreparedStatement statement = connection.prepareStatement("SELECT ? FROM ? WHERE ?=? ");
then
statement.setString(1, "column_name");
statement.setString(2, "table_name");
statement.setString(3, "column_name");
statement.setBigDecimal(4, 123);
If you are using other ORM like Hibernate or JPA, I believe there are also ways to do.

Related

SQL injection setString

Is there a possible way to inject some code in the the following statements (I tried the sleep function and it worked , but i'm looking for a way to get the table's name):
PreparedStatement statement = connection.prepareStatement(
"select password from " + USERS_TABLE_NAME + " where userid = ? and password = ?");
statement.setString(1, username_login);
statement.setString(2, password_login);
Using a prepared statement makes the code safe from SQL injection; the only way to inject some code into that query would be to tamper with USERS_TABLE_NAME somehow. I can't tell much about this as you didn't provide any code related to this, but if it's just a string constant you should be fine.
I read it can be be injectable using the ORDER BY clause !!
The example you showed above is not vulnerable to injection in the ORDER BY clause. The query doesn't even have an ORDER BY clause, or any string concatenation that could append an ORDER BY clause to the end of the query.
SQL injection can occur only if you allow untrusted content to modify the SQL query before passing it to the prepare() method.

Dynamic SQL Query in Java

I have a function
public void executeMyQuery(Connection con) {
PreparedStatement ps = con.prepareStatement("SELECT * FROM STUDENT WHERE ID = ?");
ps.setInt(1, 7);
ps.executeQuery();
}
if i will run this it will work fine. But I want to do like this.
if I will setInt it should include WHERE clause. (returns matched
row)
if I don't setInt it should exclude WHERE clause. (returns whole table)
Or is there is any way to dynamically remove or modify the string after WHERE Clause.
Disadvantages of using string based SQL for dynamic SQL
Other answers have shown how to achieve dynamic SQL using string based JDBC usage. There are many disadvantages to building SQL strings dynamically using string concatenation, including:
High risk of SQL injection if you accidentally concatenate user input to your SQL queries
Difficult to avoid syntax errors in non-trivial cases, when dynamic SQL gets more complex
Also, when you're using plain JDBC (which only supports indexed bind variables), rather than some utility such as Spring JdbcTemplate, MyBatis, jOOQ, etc. you will have to manually match ? placeholders with their corresponding indexes, which is another subtle source of errors.
Using a query builder
At some point, when you implement dynamic SQL queries more often, query builders will definitely help. The most popular ones are:
jOOQ (for dynamic SQL querying)
JPA Criteria Query (for dynamic JPAL querying)
There are many other options that are more or less well maintained. For very trivial cases (like the one in your question), you can also build your own simple predicate builder.
Disclaimer: I work for the company behind jOOQ.
You have to build your query dynamically, at the beginning of the method check whether id is null or equal 0. To make it easier you can use trick in where clause with 1=1 so where clause can be included all the time in the query.
public void executeMyQuery( Connection con, Integer id) {
String query = "SELECT *FROM STUDENT WHERE 1=1";
if(id != null){
query += "AND ID = ?";
}
PreparedStatement ps = con.prepareStatement(query);
if(id != null){
ps.setInt(1, id);
}
ps.executeQuery();}
ifNull/Coalesce work nicely for this, if you pass a null, it will select where the field equals itself.
SELECT *
FROM STUDENT
WHERE 1 = 1
and ID = ifNull(:ID, ID)
I'd also suggest something other than using ? for your variables, fine when you a couple but as you get a ton of them, difficult to keep track or modify. I've found https://github.com/dejlek/jlib/blob/master/src/main/java/com/areen/jlib/sql/NamedParameterStatement.java pretty easy, made a few modifications to fit my particular needs but SQL is much easier to read and doing substitutions in intellij db console are much easier when developing the SQL.
You can have two PreparedStatements defined in your program - one without the WHERE ID = ? clause, and another one with it.
Moreover, you are supposed to keep your PreparedStatements and re-use, so you better store them as a field, etc.
And then, when needing to get the data - call either the first prepared statement, or the second one.
Michael Dz is close to the solution in his answer, but there is a problem in the code : he calls setInt on a non existing preparedStatement.
Try something like this :
public void executeMyQuery( Connection con, int Id) {
StringBuffer sql = new StringBuffer();
sql.append("Select * FROM STUDENT");
if(Id > -1) {
sql.append(" Where ID = ?");
}
preparedStatement ps = con.prepareStatement(sql.toString());
if(ID > -1) {
ps.setInt(1, Id);
}
ps.executeQuery(); // You might want to catch the result of the query as well
}
Hope this helps !

UCASE and UPPER sql functions

I am trying to do the following query:
String query = "SELECT * FROM EMP WHERE UCASE(LAST_NAME) ";
query += "LIKE '" + lastName.toUpperCase() + "%'";
in an example of usage of an servlet to access to a database
But I am getting the error message:
Excepcion java.sql.SQLSyntaxErrorException: ORA-00904: "UCASE": invalid identifier
On the other hand, when I use the UPPER sql function, the example works but the results do not show the values of the LASTNAME column in uppercase. I do not understand what happens.
You're just comparing the upper case values, but you're selecting the actual values with select *
to get the uppercase name in your resultset you need to use UPPER in your select list, not UCASE, like this:
String query = "SELECT UPPER(LAST_NAME) AS UPPERNAME, * FROM EMP WHERE UPPER(LAST_NAME) ";
query += "LIKE '" + lastName.toUpperCase() + "%'";
What your code is doing here is building a query string named query. Once query is complete, it will be sent to the database for parsing and running.
When you are building a query to the database, you have to use the built-in database functions for the part of the query that the database is going to parse and run. So, in your example, Java is doing toUpperCase on lastName and then putting that literal into the query string that will go to the database. UPPER(LAST_NAME) is going into the query string as is, it will get passed to the database just like that and run by the database. So it needs to be a function that the database can parse and run: an Oracle function, not a Java function.
UCASE is a DB2 function & not Oracle. For Oracle, you need to use UPPER .
Second part of your question is already answered by James Z.
Having said that, I am answering because previous answers didn't pointed out SQL injection problem with the way you listed your query.
Make it a habit to always execute parametrized queries with jdbc & not by directly appending values to query string.
String query = "SELECT * FROM EMP WHERE UCASE(LAST_NAME) LIKE ? ";
Your parameter would be - lastName.toUpperCase()+"%"
SQL Injection

Insert data to sql using java issue

I'm facing an issue with insertion to SQL database from java code.
I'm using INSERT sql query using the java code to enter the data from XML file to SQL database.
You may suppose column named "Description".
Imagine there is a record in XML which contains apostrophe ('). The program crashes due to the error caused by the apostrophe which is included in the data.
I know that manually we can add another apostrophe and make it work, but imagine data of 10.000 records, how can we handle this issue?
Don't do this (string concatenation):
String sql = "insert into MyTable (description) values ('" + value + "')";
Statement st = connection.createStatement();
st.executeUpdate(sql);
Do do this (prepared statement):
PreparedStatement ps = connection.prepareStatement(
"insert into MyTable (description) values (?)"
);
ps.setString(1, value);
pt.executeUpdate();
The value will get correctly escaped for you. Not only does this protect against mishaps like the one you mentioned, it also helps defend you from SQL injection attacks.
Humorous illustration:
Source
You have two options, you should use PreparedStatement and bind your parameter(s). Or, if you really, really, want - you could use StringEscapeUtils.escapeSql(str).

Error with simple Parameterized Query - Java/ SQL

Following on from one of my previous questions to do with method design I was advised to implemented my SQL queries as a parameterized query as opposed to a simple string.
I've never used parameterized queries before so I decided to start with something simple, take the following Select statement:
String select = "SELECT * FROM ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "person");
This gives me the following error: "[SQLITE_ERROR] SQL error or missing database (near "?": syntax error)"
I then tried a modified version which has additional criteria;
String select = "SELECT id FROM person WHERE name = ? ";
PreparedStatement ps = connection.prepareStatement(select);
ps.setString(1, "Yui");
This version works fine, in the my first example am I missing the point of parameterized queries or am I constructing them incorrectly?
Thanks!
Simply put, SQL binds can't bind tables, only where clause values. There are some under-the-hood technical reasons for this related to "compiling" prepared SQL statements. In general, parameterized queries was designed to make SQL more secure by preventing SQL injection and it had a side benefit of making queries more "modular" as well but not to the extent of being able to dynamically set a table name (since it's assumed you already know what the table is going to be).
If you want all rows from PERSON table, here is what you should do:
String select = "SELECT * FROM person";
PreparedStatement ps = connection.prepareStatement(select);
Variable binding does not dynamically bind table names as others mentioned above.
If you have the table name coming in to your method as a variable, you may construct the whole query as below:
String select = "SELECT * FROM " + varTableName;
PreparedStatement ps = connection.prepareStatement(select);
Parameterized queries are for querying field names - not the table name!
Prepared statements are still SQL and need to be constructed with the appropriate where clause; i.e. where x = y. One of their advantages is they are parsed by the RDMS when first seen, rather than every time they are sent, which speeds up subsequent executions of the same query with different bind values.

Categories

Resources