Here is my code. I am trying to use a variable instead of a column name in here
But I get below exception. How can I resolve this error?
You can't bind table/column names in a prepared statement, nor would you normally want to allow this. Here is a working version of your code:
String query = "UPDATE report SET itemno = ?";
pst = (PreparedStatement) con.prepareStatement(query);
pst.setInt(1, dqty);
pst.executeUpdate();
Notes:
You almost certainly want to add a WHERE clause to your update, without which it would affect every record in the table. With prepared statements, you don't need to worry about escaping your literal data. Just let Java handle this for you.
If you really need the ability to update other table/column combinations, then just create other statements for that. One size fits all works at 7-Eleven, but not JDBC, since you might SQL injected.
Related
I have been working with a JDBC for the past couple of weeks and I have come upon a problem that I figure will have subjective answers. Let's suppose we want to create a method that dynamically constructs a SQL statement that is to be executed based upon what a user inputs into some GUI. Based on what the user has put into the GUI, we need to gather that information, validate it, then pass it into the database. That is, if the user has left any field empty, we simply do not add any extra conditionals to the SQL statement.
For example if the user left the hypothetical column "name" blank (and the table automatically generates primary keys) we might write
INSERT INTO <tableName>;
to add a new row to the table.
Alternatively if the user has given a name, we write,
INSERT INTO <tableName> (name) VALUES (?);
.
With that context given lets suppose I construct a method that dynamically creates this SQL statement:
public void addToDatabase(){
Connection connection = createConnectionToDatabase();
String str = "INSERT INTO <tableName>";
if(!name.isBlank()){
str += " (name) VALUES (?)"
}
str += ";";
PreparedStatement statement = connection.prepareStatement(str);
if(!name.isBlank()){
statement.setString(1, name);
}
statement.execute();
connection.close();
If you notice, we check if name is blank twice - which I find rather annoying since it should only be checked once in my opinion. The first time we check if name is blank is to construct the proper string to be placed into the SQL statement. The second time we check if the name is blank is to confirm if we need to pass the parameter into the prepared statement. This creates a sort of catch-22 that forces us to check if the name is blank twice which I do not like. Are there any better ways of handling this situation to only have to check for the name once?
I found a few other answers stating that there is no better way of doing this kind of dyamic SQL statements but I don't like that answer and am hoping for a better one. Thanks.
what you want is equivalent to
String str = "INSERT INTO <tableName> name values (?)";
PreparedStatement statement = connection.prepareStatement(str);
if(!name.isBlank()){
statement.setString(1, name);
}
else {
statement.setNull(1, Types.VARCHAR);
}
I want to insert a row to table using JDBC. But table has a big number of columns, so I don't want to specify all of them into a statement. Is there another way of specifying column values?
INSERT INTO TABLE_NAME VALUES(?, ?, ?, ..., ?)
I hope there is another way to do it without a big number of placeholders.
EDITED:
I mean, can I just edit query like ... meaning that it expects a many values and dynamically set values with incremented index or something else. And yes as #f1sh mentioned the problem is in the number of placeholders, but not in case of laziness, more in case of convenience and cleanliness.
P.S.: for some funny enterprise requirements reasons I can not use JPA frameworks :)
EDIT
Another option is to write your own or use SqlBuilder
SqlBuilder changes that whole scenario by wrapping the SQL syntax within very lightweight and easy to use Java objects which follow the "builder" paradigm (similar to StringBuilder). This changes many common SQL syntactical, runtime errors into Java compile-time errors! Let's dive right in to some quick examples to to see how it all works.
You can use Spring's NamedParameterJdbcTemplate and use variable names
String SQL = INSERT INTO TABLE_NAME VALUES((:id),...);
MapSqlParameterSource parameters = new MapSqlParameterSource();
parameters.addValue("id", "idValue");
//...
namedParameterJdbcTemplate.update(SQL, parameters) > 0;
Template class with a basic set of JDBC operations, allowing the use of named parameters rather than traditional '?' placeholders.
If you can (safely) use dynamic query using Statement:
Statement stmt = (Statement) con.createStatement(“SELECT username, password FROM users WHERE username='” + user + “‘ AND password='” + pass + “‘ limit 0,1”);
ResultSet rs = stmt.executeQuery();
You can use it only on closed values so there will be no option for SQL injection
I'm trying to use jOOQ to generate the following SQL statement:
SELECT id, name
FROM students
ORDER BY id DESC
LIMIT 50;
To generate the above statement with jOOQ:
String sql = DSL.using(SQLDialect.POSTGRES).select(
field("id"),
field("name"))
.from("students")
.orderBy(field("id").desc())
.limit(inline(50))
.getSQL();
But I get the following:
select id, name from students order by id desc limit 50 offset ?
How do I remove the OFFSET clause? I know that I can specify the offset value to 0, which is same as omitting the OFFSET clause, but I want to know whether I can completely remove it from the generated SQL statement.
Thank you.
You're right, jOOQ 3.4.2 currently renders an OFFSET clause for PostgreSQL, regardless of whether it was specified by users through the API or not. That could probably be improved. I've created issue #3577 for this.
Currently, there isn't really an "easy" way to change the generated SQL in this case. You could be implementing an ExecuteListener, and patch the generated SQL - but you'd also have to get rid of the bind value.
I have to execute below query through JDBC call
select primaryid from data where name in ("abc", adc", "anx");
Issue is inside in clause I have to pass 11000 strings. Can I use prepared statement here? Or any other solution any one can suggest. I dont want to execute the query for each record, as it is consuming time. I need to run this query in very less time.
I am reading the strings from an XML file using DOMParser. and I am using sql server db.
I'm just wondering why you would need to have a manual set of 11,000 items where you need to specify each item. It sounds like you need to bring the data into a staging table
(surely it's not been selected from the UI..?), then join to that to get your desired resultset.
Using an IN clause with 11k literal values is a really bad idea - off the top of my head, I know one major RDBMS (Oracle) that doesn't support more than 1k values in the IN list.
What you can do instead:
create some kind of (temporary) table T_NAMES to hold your names; if your RDBMS doesn't support "real" (session-specific) temporary tables, you'll have to add some kind of session ID
fill this table with the names you're looking for
modify your query to use the temporary table instead of the IN list: select primaryid from data where name in (select name from T_NAMES where session_id = ?session_id) or (probably even better) select primaryid from data join t_names on data.name = t_names.name and t_names.session_id = ?session_id (here, ?session_id denotes the bind variable used to pass your session id)
A prepared statement will need to know the number of arguments in advance - something along the lines of :
PreparedStatement stmt = conn.prepareStatement(
"select id, name from users where id in (?, ?, ?)");
stmt.setInt(1);
stmt.setInt(2);
stmt.setInt(3);
11,000 is a large number of parameters. It may be easiest to use a 'batch' approach as described here (in summary - looping over your parameters, using a prepared statement
each time)
Note - if your 11,000 strings are the result of an earlier database select, then the best approach is to write a stored procedure to do the whole calculation in the database (avoiding passing the 11,000 strings back and forth with your code)
You can merge all your parameter strings into one bitg string separating by ';' char
bigStrParameter=";abc;adc;anx;"
And use LOCATE to find substring.
select primaryid from data where LOCATE(concat(';',name,';'),?)>=0;
I'm fairly new to SQL and I'm currently reworking a java program that another
programmer has developed. When I print one of his query select statements the script contains sql syntax:
SELECT * from database WHERE id = ?
I just want know what =? is supposed to do? I've been googling around and I can't find any relevant answer.
It's not a SQL notation, but a JDBC (Java Database Connectivity) notation. The ? gets replaced with a parameter that is specified separately. Using this approach, instead of trying to substitute the parameter yourself into the string, helps prevent the risk of SQL injection.
The ? is a place holder, a parameter, so that you can pass it in dynamically and return different results for different parameters.
Somewhere in the code you should see that he adds the parameter to the Statement object and execute it.
Most likely you are using a tool that will replace the "?" with an actual value. I've seen this in other tools before such as SQL DTS (Data Transformation Services)... but that's showing how old I am :)
The ? is not part of the SQL language.
The ? is a place holder used in SQL queries when used with JDBC Prepared statement. Using a prepared statement has advantages over the normal statement specially when you use it repeatedly (say in a loop).
Here is an example :
PreparedStatement ps =
connection.prepareStatement("select name from users where user_name = ?");
ps.setString(1, "user1");
the "?" gets replace by "user1" when the query is run and the first name of the user with user name "user1" is returned.