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);
}
Related
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.
I have to update table using values from a data file which contains all the rows. Now I am using JDBC batches. Data files contains 100s of columns and millions of rows.
For e.g. data file contains 3 columns two rows to make it simple
1,ABC,DEF
2.GHI,JKL
PreparedStatement pstmt = connection.prepareStatement(insert);
//how to find type
pstmt.setInt(1, 2);
pstmt.setString(2, "GHI");
pstmt.setString(3, "JKL");
pstmt.addBatch();
pstmt.executeBatch();
Now my question is at run time based on the data coming from data file how do I find that I need to call setInt or setString and more importantly how many times I need to call setXXX for that addBatch(). This seems like for each table I need to have dedicated preparedStatements. More importantly I need to find how many times I should call setObject at run based on the number of columns which is in data ile? Is there anyway I can make this generic?
I am new to JDBC please guide. Thanks in advance.
You can use setObject(int index, Object obj). JDBC then determines the correct type.
The PreparedStatement has a method setObject(int, Object). The documentation states
If arbitrary parameter type conversions are required, the method setObject should be used with a target SQL type.
If you have an SQL statement like
Select * From table Where value1 = ? and value2 = ?
you have to call the setXXX methods two times. They are used to specify the wildcard values (marked ny ?) for the SQL statement that the PreparedStatement instance represents. The number of calls is therefore dependent of your SQL statement that is referenced by your variable insert. The int argument of the setXXX methods refers to the position of the variable in the SQL statement with setXXX(1, object) referring to the first wildcard and so on.
Of course, you have to repeat the same amount of calls to setXXX for each query you add the the batch that you want to execute at the end.
You can use like below snippet. Check statement.setObject documentation for further details. The "rs" in the below snippet is a resultset got by executing some query from one table. The "query" in the below snippet is some insert or update query into different table. The below example states selection from one table and insertion into some other table while dynamically identifying the column type. Note: Table column types should match else exception will be thrown.
PreparedStatement statement = connection.prepateStatement( query );
ResultSetMetaData rsmd = rs.getMetaData();
while( rs.next() )
{
for( int i = 1 ; i <= rsmd.getColumnCount() ; i++ )
{
statement.setObject( i, rs.getObject(i), rsmd.getColumnType(i) );
}
}
I'm creating a search form for my application.
In it user selects fields that should be used in filtering data.
the number fields is variable so I don't know how many ? should be in where clause of SQL query.
How can I use preparedStatement with variable number of conditions in where clause?
Thanks
if you want to add variable number of conditions in where clause use
StringBuilder (StringBuffer if its multithreaded env.) and and run time depending upon your conditions concate/append to your string.
like
StringBuilder query = new StringBuilder("Select id, name from Student ");
if(args >0)
{
query.append(" where "); //and add more args.
and later create prepared statement with this query by converting it to string
PrepareStatement(query.toString());
PrepardStatements don't support variable numbers of conditions. What some frameworks do, is they cache each PreparedStatement in a Map where the key is the query.
So each time you want to run a query, you need to build the string to create the PreparedStatement, check if you have it in the map (and reuse it) or create a new one, and add it to the map.
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.
I am trying to do this:
while (rs.next()) {
int id = rs.getInt(1);
ResultSet innerRs = stmt.executeQuery("select something from sometable where id =" + id + ";");
String one = innerRs.getString(1);
String two = rs.getString(2); //result set is already closed thrown here.
}
but I am getting the error "result set already closed". I am using Postgresql. I can't really figure out another way to do this that isn't very labour intensive. How can I get around this error?
You don't need two resultsets. Just learn the SQL JOIN clause so that you can get the desired data from two different tables in a single SQL query.
If you really need to, then creating a separate statement instead of reusing an existing one is indeed the way to go. Reusing it would namely force all previously opened cursors (resultsets) to be closed.
You use stmt.executeQuery(), did you use stmt to execute rs? If so, you need a new statement as creating a query on one you used already forces it to close, resulting in this error.