Create table as select with parameter using jdbcTemplate - java

I want to use jdbcTemplate to create table based on another table under condition. I have postgres database. When I execute this and pass parameter:
String SQL = "create table test as (select * from users where countryId =?)";
jdbcTemplate.update(SQL, new Object[] {3})
I receive table test with all columns from users table but with no rows.
However, when I execute this:
String SQL = "create table test as (select * from users where countryId =3)";
jdbcTemplate.update(SQL)
I receive test table with rows where countryId = 3, so that is what I was expecting to receive in the first solution.

Your passing of the bind variable is not correct, but it does not play any role.
You simple can not use a bind variable in a data definition statement as you immediately see in the triggered error
Caught: org.springframework.jdbc.UncategorizedSQLException:
PreparedStatementCallback; uncategorized SQLException for SQL
[create table test as (select * from users where countryId =?)];
SQL state [72000]; error code [1027];
ORA-01027: bind variables not allowed for data definition operations
So you have two options, either concatenate the statement (which is not recommended due to the danger of SQL injection)
or split the statement in two parts:
// create empty table
sql = "create table test as (select * from users where 1 = 0)";
jdbcTemplate.update(sql)
// insert data
sql = "insert into test(countryId, name) select countryId, name from users where countryId =?";
updCnt = jdbcTemplate.update(sql, new SqlParameterValue(Types.INTEGER,3));
Note that in the insert statement you can see the correct way of passing an interger value of 3 as a bind variable.

You can follow below approach as well:-
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS employee_tmp (id INT NOT NULL)");
List<Object[]> employeeIds = new ArrayList<>();
for (Integer id : ids) {
employeeIds.add(new Object[] { id });
}
jdbcTemplate.batchUpdate("INSERT INTO employee_tmp VALUES(?)", employeeIds);
Here you may query with 2 operations to avoid SQL injection.

You are using method update from jdbcTemplate in a wrong way.
Try with this:
String SQL = "create table test as (select * from users where countryId = ?)";
jdbcTemplate.update(SQL, 3);

Related

Java dynamically generate SQL query - ATHENA

I am trying to generate sql query based on user input. There are 4 search fields on the UI:
FIRST_NAME, LAST_NAME, SUBJECT, MARKS
Based on user input I am planning to generate SQL query. Input can be of any combination.
eg: select * from TABLE where FIRST_NAME="some_value";
This query needs to be generated when FIRST_NAME is given and other fields are null
select * from TABLE where FIRST_NAME="some_value" and LAST_NAME="some_value";
This query needs to be generated when FIRST_NAME and LAST_NAME are given and other fields are null
Since there are 4 input fields, number of possible queries that can be generated are 24 (factorial of 4).
One idea is to write if condition for all 24 cases.
Java pseudo code:
String QUERY = "select * from TABLE where ";
if (FIRST_NAME!=null) {
QUERY = QUERY + "FIRST_NAME='use_input_value';"
}
if (LAST_NAME!=null) {
QUERY = QUERY + "LAST_NAME='use_input_value';"
}
if (SUBJECT!=null) {
QUERY = QUERY + "SUBJECT='use_input_value';"
}
if (MARKS!=null) {
QUERY = QUERY + "MARKS='use_input_value';"
}
I am not able to figure out how to generate SQL queries with AND coditions for multiple Input values.
I have been through concepts on dynamically generate sql query but couldn't process further.
Can someone help me on this.
FYI: I have been through How to dynamically generate SQL query based on user's selections?, still not able to generate query string based on user input.
Let's think about what would happen if you just ran the code you wrote and both FIRST_NAME and LAST_NAME are provided. You'll wind up with this:
select * from TABLE where FIRST_NAME='use_input_value';LAST_NAME='use_input_value';
There are two problems here:
The query is syntactically incorrect.
It contains the literals 'use_input_value' instead of the values you want.
To fix the first problem, let's first add and to the start of each expression, and remove the semicolons, something like this:
String QUERY = "select * from TABLE where";
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME='use_input_value'";
}
Notice the space before the and. We can also remove the space after where.
Now the query with both FIRST_NAME and LAST_NAME will look like this:
select * from TABLE where and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value'
Better but now there's an extra and. We can fix that by adding a dummy always-true condition at the start of the query:
String QUERY = "select * from TABLE where 1=1";
Then we append a semicolon after all the conditions have been evaluated, and we have a valid query:
select * from TABLE where 1=1 and FIRST_NAME='use_input_value' and LAST_NAME='use_input_value';
(It may not be necessary to append the semicolon. Most databases don't require semicolons at the end of a single query like this.)
On to the string literals. You should add a placeholder instead, and simultaneously add the value you want to use to a List.
String QUERY = "select * from TABLE where";
List<String> args = new ArrayList<>();
if (FIRST_NAME!=null) {
QUERY = QUERY + " and FIRST_NAME=?";
args.add(FIRST_NAME);
}
After you've handled all the conditions you'll have a string with N '?' placeholders and a List with N values. At that point just prepare a query from the SQL string and add the placeholders.
PreparedStatement statement = conn.prepareStatement(QUERY);
for (int i = 0; i < args.size(); i++) {
statement.setString(i + 1, args[i]);
}
For some reason columns and parameters are indexed starting at 1 in the JDBC API, so we have to add 1 to i to produce the parameter index.
Then execute the PreparedStatement.

Spring JDBC Template - Oracle DB Function returns null

I have my Oracle DB Stored Function as below:
CREATE OR REPLACE FUNCTION FN_EMP_CNT (EMP_ID NUMBER) RETURN NUMBER
IS
OLD_COUNT NUMBER(5) DEFAULT 0;
NEW_COUNT NUMBER(5) DEFAULT 0;
BEGIN
SELECT
COUNT(EMP_ID) INTO OLD_COUNT
FROM
OLD_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed';
SELECT
COUNT(EMP_ID) INTO NEW_COUNT
FROM
NEW_DEPT
WHERE
EID = EMP_ID
AND DEPT_STAT='Closed'
RETURN (NEW_COUNT + OLD_COUNT);
END;
When I use the below sql query directly it returns the correct number as 2:
SELECT FN_EMP_CNT(123) FROM DUAL;
But when I use Spring JDBC Template for retrieving the data it returns null.
int noOfEmps = jdbcTemplate.queryForObject("SELECT FN_EMP_CNT(?) FROM DUAL", new Object[] { empID}, Integer.class);
The most probable cause is that you use a wrong order of parameters, see Javadoc queryForObject
queryForObject(java.lang.String sql, java.lang.Class<T> requiredType, java.lang.Object... args)
Query given SQL to create a prepared statement from SQL and a list of
arguments to bind to the query, expecting a result object.
So use first the required return type followed by the parameter
This works for my fine
sql = 'SELECT FN_EMP_CNT(?) FROM DUAL'
res = jdbcTemplate.queryForObject(sql, Integer.class, 4)
HTH

select scope_identity() using createSQLQuery in Hibernate

I am forced to use createSQLQuery to insert values into tables with an Identity column (the first column and the primary key) using hibernate. Using hibernate classes are not an option since the tables are created on the fly for each customer that is added to the system. I have run the query and it successfully inserts into the table. I then execute a "select scope_identity()" and it always returns null. "select ##Identity" works but that is not guaranteed to be the correct one. I have also tried to append "select scope_identity()" to the insert query. Then I tried query.list() and query.uniqueResult() both of which throw the hibernate exception of "No Results ..."
Session session = DatabaseEngine.getSessionFactory().openSession();
String queryString = "insert into table1 (dataid) values (1)"
SQLQuery query = session.createSQLQuery(insertQueryString);
query.executeUpdate();
query = session.createSQLQuery("select scope_identity()");
BigDecimal entryID = (BigDecimal)query.uniqueResult();
The simple example table is defined as follows:
"CREATE TABLE table1 (EntryID int identity(1,1) NOT NULL," +
"DataID int default 0 NOT NULL, " +
"PRIMARY KEY (EntryID))";
Is there a way I am missing to use scope_identity() with createSQLQuery?
Actually the SQLServerDialect class used by Hibernate uses the same "scope_identity()" too.
The reason why it's not working is because you need to execute those in the same statement or stored procedure.
If you execute the scope_identity() call in a separate statement, SQL Server will not be able to give you last inserted identity value.
You cannot do it with the SQLQuery, even Hibernate uses JDBC to accomplish this task. I wrote a test on GitHub to emulate this and it works like this:
Session session = entityManager.unwrap(Session.class);
final AtomicLong resultHolder = new AtomicLong();
session.doWork(connection -> {
try(PreparedStatement statement = connection.prepareStatement("INSERT INTO post VALUES (?) select scope_identity() ") ) {
statement.setString(1, "abc");
if ( !statement.execute() ) {
while ( !statement.getMoreResults() && statement.getUpdateCount() != -1 ) {
// do nothing until we hit the resultset
}
}
try (ResultSet rs = statement.getResultSet()) {
if(rs.next()) {
resultHolder.set(rs.getLong(1));
}
}
}
});
assertNotNull(resultHolder.get());
The code uses Java 8 lambdas instead of anonymous classes, but you can easily port it to Java 1.7 too.

List of columns in sql query

I have a query using various joins, and I just need the list of columns which are returned by this query. I done it in java, by asking only one row with rownum=1 and getting column name for value.The problem is if there is no data returned by that query.
For ex.
select * from something
and if there is any data returning by this query then it will return col1,col2,col3.
But if there is no data returned by this query, then it will throw error.
What I need is
Is there any way that I can run
desc (select * from something)
or similar to get list of columns returned by query.
It can be in sql or JAVA. Both methods are acceptable.
In my application, user passes the query, and I can add wrapper to the query but I cant modify it totally.
The flow of application is
query from user -> execute by java and get one row -> return list of columns in the result set.
you can use ResultSetMetaData of resultset
for example :
ResultSet rs = stmt.executeQuery("SELECT a, b, c FROM TABLE2");
ResultSetMetaData rsmd = rs.getMetaData();
int countOfColumns = rsmd.getColumnCount();
for(int i = 1; i <= countOfColumns ; i++ )
System.out.println(rsmd.getColumnName(i));
you could maybe convert your query to a view, you can then see the columns in the view by querying user_tab_columns
select * from user_tab_columns
The Oracle equivalent for information_schema.COLUMNS is USER_TAB_COLS for tables owned by the current user, ALL_TAB_COLS or DBA_TAB_COLS for tables owned by all users.
Tablespace is not equivalent to a schema, neither do you have to provide the tablespace name.
Providing the schema/username would be of use if you want to query ALL_TAB_COLS or DBA_TAB_COLS for columns OF tables owned by a specific user. in your case, I'd imagine the query would look something like:
String sqlStr= "
SELECT column_name
FROM all_tab_cols
WHERE table_name = 'users'
AND owner = ' || +_db+ || '
AND column_name NOT IN ( 'password', 'version', 'id' )
"
Note that with this approach, you risk SQL injection.

problem in setString method.in retrieving data from the table

i have two tables "Table1" with columns user_name,Password and course ID and another table "course" with columns course_id,course_name.I have used the following code to display the course ID from Table1 according to the user_name received from the login page.using ResultSet rs1.now i want to retrieve the course_name from the table "course" according to the course ID receieve from "Table1".for that in the second query pstmt2.setString(1, ); what parameter i should use to get the course_id value from the previous query
HttpSession sess=request.getSession();
String a=(String)sess.getAttribute("user");
String b=(String)sess.getAttribute("pass");
try {
Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
Connection con=DriverManager.getConnection("jdbc:odbc:ggg");
Statement st = con.createStatement();
String query="select * from Table1 where user_name=?";
PreparedStatement pstmt=con.prepareStatement(query);
pstmt.setString(1,a);
ResultSet rs1=pstmt.executeQuery();
while(rs1.next())
out.println("<h3>COURSE ID: "+rs1.getString("course ID")+"<h3>");
String query2="SELECT * from course where course_id=?";
PreparedStatement pstmt2=con.prepareStatement(query2);
pstmt2.setString(1,);
ResultSet rs2=pstmt2.executeQuery();
while(rs2.next())
{
out.println("<h3>course name: "+rs2.getString("course_name")+"<h3>");
}
why do you go for two turns of database hit, even though you created one time connection object.
modify the query as below
SELECT * from course where course_id = (select course_id from Table1 where user_name=?);
from this query you noneed to give input of courseid also.
No need to hit database twice to get the results that you need. use the query
Select table1.course_id, course.course_name from table1, course where table1.course_id=course_id and table1.user_name=?
This should set the course_id parameter:
pstmt2.setString(1,rs1.getString("course_id"));
Or, as I see the "course_id" column may have a different name in "Table1":
pstmt2.setString(1,rs1.getString("course ID"));
As the other post mentioned there's no need to go to another set of query. Try this example query:
SELECT course.course_id, course.course_name
FROM table1 t1
INNER JOIN course c
ON t1.course_id = c.course_id
WHERE t1.user_name = ?;
Now if you insist your coding the parameter o your pstmt2.setString(1,); is:
pstmt2.setString(1,rs1.getString("course_id")); //or course ID defending on your column name

Categories