PreparedStatement,using one parameter for multiple "?" - java

I have a insert if not exists query as below.
BEGIN
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = ? or subject = ?)
BEGIN
INSERT INTO tbl_sampleTable VALUES (?,?)
END
END
I am executing the above query with JDBC PreparedStatement as below
pst.setString(1, name);
pst.setString(2, subject);
pst.setString(3, subject);
pst.setString(4, name);
pst.executeUpdate();
I am getting these name and subject as method parameters, is there anyway i can provide values for multiple "?" with same parameter as they are same, instead of mentioning them two times each.
Edit: I don't use spring or any other framework, if it is relevant.

JDBC doesn't support named parameters, but Spring JDBC provides this functionality with NamedParameterJdbcTemplate

Just in this case you might use SQL variables. It is not a general solution.
And also many SQL vendor specific variants know such insert-when-not-exists constructs, not needing such archaic code.
BEGIN
DECLARE #MyName varchar(100);
DECLARE #MySubject varchar(100);
SET #MyName = ?;
SET #MySubject = ?;
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = #MyName OR subject = #MySubject)
BEGIN
INSERT INTO tbl_sampleTable(subject, name) VALUES (#MySubject, #MyName)
END
END

You need to add some wrapper, without using Spring (NamedParameterJdbcTemplate) you can try other as HTTP-RPC framework
The org.httprpc.sql.Parameters class provided by the HTTP-RPC framework brings named parameter support to JDBC. The parse() method of this class is used to create a Parameters instance from a JPA-like SQL query; for example:
SELECT * FROM user WHERE first_name LIKE :pattern or last_name LIKE :pattern
It takes a string or reader containing the query text as an argument:
Parameters parameters = Parameters.parse(sqlReader);
The getSQL() method of the Parameters class returns the processed query in standard JDBC syntax. This value can be used in a call to Connection#prepareStatement():
PreparedStatement statement = connection.prepareStatement(parameters.getSQL());
Parameter values are specified via the put() method:
parameters.put("pattern", pattern);
The values are applied to the statement via the apply() method:
parameters.apply(statement);

Usefull trick in this situation is to declare variables.
You bind the values to the variables only once and you can use them several times in your PL/SQL block.
DECLARE
l_name tbl_sampleTable.name%TYPE := ?;
l_subject tbl_sampleTable.subject%TYPE := ?;
BEGIN
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = l_name or subject = l_subject )
BEGIN
INSERT INTO tbl_sampleTable (name,subject)
VALUES (l_name ,l_subject )
END
END

Related

Java PreparedStatement: any way to use indexed or named parameters? [duplicate]

I have a insert if not exists query as below.
BEGIN
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = ? or subject = ?)
BEGIN
INSERT INTO tbl_sampleTable VALUES (?,?)
END
END
I am executing the above query with JDBC PreparedStatement as below
pst.setString(1, name);
pst.setString(2, subject);
pst.setString(3, subject);
pst.setString(4, name);
pst.executeUpdate();
I am getting these name and subject as method parameters, is there anyway i can provide values for multiple "?" with same parameter as they are same, instead of mentioning them two times each.
Edit: I don't use spring or any other framework, if it is relevant.
JDBC doesn't support named parameters, but Spring JDBC provides this functionality with NamedParameterJdbcTemplate
Just in this case you might use SQL variables. It is not a general solution.
And also many SQL vendor specific variants know such insert-when-not-exists constructs, not needing such archaic code.
BEGIN
DECLARE #MyName varchar(100);
DECLARE #MySubject varchar(100);
SET #MyName = ?;
SET #MySubject = ?;
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = #MyName OR subject = #MySubject)
BEGIN
INSERT INTO tbl_sampleTable(subject, name) VALUES (#MySubject, #MyName)
END
END
You need to add some wrapper, without using Spring (NamedParameterJdbcTemplate) you can try other as HTTP-RPC framework
The org.httprpc.sql.Parameters class provided by the HTTP-RPC framework brings named parameter support to JDBC. The parse() method of this class is used to create a Parameters instance from a JPA-like SQL query; for example:
SELECT * FROM user WHERE first_name LIKE :pattern or last_name LIKE :pattern
It takes a string or reader containing the query text as an argument:
Parameters parameters = Parameters.parse(sqlReader);
The getSQL() method of the Parameters class returns the processed query in standard JDBC syntax. This value can be used in a call to Connection#prepareStatement():
PreparedStatement statement = connection.prepareStatement(parameters.getSQL());
Parameter values are specified via the put() method:
parameters.put("pattern", pattern);
The values are applied to the statement via the apply() method:
parameters.apply(statement);
Usefull trick in this situation is to declare variables.
You bind the values to the variables only once and you can use them several times in your PL/SQL block.
DECLARE
l_name tbl_sampleTable.name%TYPE := ?;
l_subject tbl_sampleTable.subject%TYPE := ?;
BEGIN
IF NOT EXISTS (SELECT * FROM tbl_sampleTable WHERE name = l_name or subject = l_subject )
BEGIN
INSERT INTO tbl_sampleTable (name,subject)
VALUES (l_name ,l_subject )
END
END

Inserting into database with auto increment

I need to store some values into my table using prepared statements.
I have a column in my table called id, and it is set to auto increment. I do not need any input from the user for this column.
The other values are all gotten from the user.
Edit: There are 2 IDs. One from the user and one that is auto generated from the database. The one automatically generated by the database is the primary key.
Am I doing this right?
Thanks.
Prepared Statement:
DELIMITER $$
CREATE DEFINER=`root`#`localhost` PROCEDURE `insert_details`(IN id varchar(45),IN username varchar(45),IN name varchar(45))
BEGIN insert into details (id,username,name) values (id ,username,name);
END
Code:
PreparedStatement preparedStatement = connect.prepareStatement("CALL INSERT_DETAILS(?,?,?)");
preparedStatement.setLong(1, userID);
preparedStatement.setString(2, username);
preparedStatement.setString(3, name);
preparedStatement .executeUpdate();
Remove the id field from your prepared statement.
Because it will conflict the field data so problem may occur.
You can simply omit auto-generated field from the prepared statement as MySQL will set this value for you. In your example, your parameter names are colliding with table attribute names. The convention that I follow is to prefix parameter names with a p_ or similar name mangling.
The prepared statement should look more like the following:
DELIMITER $$
CREATE
DEFINER=`root`#`localhost`
PROCEDURE `insert_details`(
IN p_user_id VARCHAR(45),
IN p_username VARCHAR(45),
IN p_name VARCHAR(45))
BEGIN
INSERT
INTO details(id, username, name)
VALUES (p_user_id, p_username, p_name);
END $$

using java variable in sql statement

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.

Eliminating quotes in prepared statement in mysql n java

I have a prepared statement like this
stmt = select * from table_name where id IN (?);
Once I pass the parameters the stmt looks like way
stmt = select * from table_name where id IN ('1,2,3');
There is no error while executing the query. However the resultset is returned only for the id=1. Is there some way I can eliminate the quotes / get the resultset for all these id's.
stmt = select * from table_name where id IN (?);
select GROUP_CONCAT(id) id from table ;
if(rs.next()){
stmt.setString(1,rs.getString("id"));
stmt.executeQuery();
}
Thanks in advance.
It's not clear what the ID type is, but I believe you should actually be preparing a statement with each possible value as a separate parameter:
select * from table_name where id IN (?, ?, ?)
Then add the three values for the three parameters. It's a common problem with parameterized SQL - when you want to be able to specify a variable number of values, you need to vary the SQL. There may be a MySQL-specific way of coping with this (like table-valued parameters in SQL Server 2008) but I don't believe there's a generic JDBC way of doing this.

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