PreparedStatement for select count [duplicate] - java

This question already has answers here:
passing table and column name dynamically using bind variables
(2 answers)
Closed 8 years ago.
I have a program that will count the number of records from a variety of data sources. I have the database names and table names stored in array lists called database_names and table_names respectively. I am unable to get this to run:
for (int i = 0; i < table_names.size(); i++) {
String query = "select count(1) from ?.?";
PreparedStatement stmt = connection.prepareStatement(query);
stmt.setString(1, database_names.get(i));
stmt.setString(2, table_names.get(i));
ResultSet rs = stmt.executeQuery();
}
I get an ORA-00903: invalid table name error. I have set print statements to ensure that my database_names.get(i) and table_names.get(i) were printing out the right values. They were, and if I hardcode the database name and table name into my query string, the program is able to count the records.
How can I properly prepare my query statement so that it is of the form:
select count(1) from database_name.table_name

Oracle says: When you prepare a SQL statement or PL/SQL block that contains input data to be supplied at runtime, placeholders in the SQL statement or PL/SQL block mark where data must be supplied.
Schema and table name are not data, but data structures. During prepare phase Oracle parses statement, checks privileges. optimises plan. This is not possible without schema and tablename.
Oracle uses bind variables in execution phase, when your statement is prepared.
In your case you have to create new PreparedStatement for each table

Related

Java PreparedStatement is Undesirably Passing Lower-Case String Names to Case-sensitive PostgreSQL Database in SQL Query [duplicate]

This question already has answers here:
PostgreSQL "Column does not exist" but it actually does
(6 answers)
Error: Column does not exist
(1 answer)
What's the problem with deleting a row from database?
(1 answer)
Java SQL "ERROR: Relation "Table_Name" does not exist"
(4 answers)
Closed 3 years ago.
I am using a Java PreparedStatement object to insert rows into a Postgres database table. (my code is below:)
...
// define values to insert
LocalDateTime localDateTime = LocalDateTime.now();
int num = 3;
double frac = 0.8;
//create PreparedStatement
PreparedStatement st = conn.prepareStatement("INSERT INTO TABLE_Final_Records(Datetime, Number_n, Fraction_f)"
+ "VALUES (?,?,?)" );
// set placeholder ("?") values as Postgres types
st.setObject(1, localDateTime);
st.setInt(2, num);
st.setDouble(3, frac);
// execute statement
st.executeUpdate();
st.close();
...
However, when I execute the above code I get the error (via a toString() of a caught SQLException):
org.postgresql.util.PSQLException: ERROR: relation
"table__final__records" does not exist.
Where each uppercase character specified in the code above appears as lower case in the text of the thrown exception.
If I manually rename the database table to be all lower case, the error is eliminated, without changing the code. However it is replaced with:
org.postgresql.util.PSQLException: ERROR: column "datetime" of
relation "table_final_records" does not exist
An obvious work-around would be to manually convert all uppercase characters in all the table and column names in the Postgres database to lower case, however I would like to know how to force case-sensitivity on such statements so that PostgreSQL database table & column names with uppercase characters can be properly recognized and updated.
Thanks
You could work around the issue by quoting the identifiers in the query:
PreparedStatement st = conn.prepareStatement(
"INSERT INTO \"TABLE_Final_Records\"(\"Datetime\", \"Number_n\", \"Fraction_f\")"
+ " VALUES (?,?,?)"
);
But bottom line: the issue that you are getting denotes that your database table and columns were created with double quotes surrounding the idenfiers. Generally, you want to avoid quoting the identifiers when you declare them, since this makes them case sensitive - which, as you are experiencing, makes writing query tedious and lengthy.

Java PreparedStatement SQL syntax error [duplicate]

This question already has answers here:
Java PreparedStatement complaining about SQL syntax on execute()
(6 answers)
Closed 6 years ago.
This is a really weird error that only started appearing today. When I use a prepared statement with ? for parameters, I get an error, but when I use it without parameters, it works just fine.
Here is the error-causing code:
String table = "files";
Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
PreparedStatement prep = conn.prepareStatement("SELECT * FROM ?");
prep.setString(1, table);
ResultSet rs = prep.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("file_name"));
}
This produces the following error:
Exception in thread "main" com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''files'' at line 1
Also, changing it to the following works just fine:
String table = "files";
Connection conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
PreparedStatement prep = conn.prepareStatement("SELECT * FROM " + table);
ResultSet rs = prep.executeQuery();
while(rs.next()) {
System.out.println(rs.getString("file_name"));
}
This doesn't seem to be making a whole lot of sense. Any ideas?
Tried it on another table and got more weired results.
This works and logs the admin in correctly:
String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE " + column + " = '" + hashedPassword + "'";
PreparedStatement prepared = connection.prepareStatement(sql);
The following doesn't cause errors, but returns a message saying that the password entered is incorrect (it's correct - I double triple checked).
String sql = "SELECT * FROM " + ADMIN_AUTH_TABLE + " WHERE ? = ?";
PreparedStatement prepared = connection.prepareStatement(sql);
prepared.setString(1, column);
prepared.setString(2, hashedPassword);
Got it: use ? for values.
Also, the answer here helped.
Bind parameters cannot be used for identifiers in the SQL statement. Only values can supplied through bind placeholders.
This will work:
SELECT foo FROM bar WHERE id = ?
This will not work, because the table name is an identifier
SELECT foo FROM ? WHERE id = 2
You can't supply a column name, because column names are also identifiers.
A statement like this will run, but it may not do what you think it does.
SELECT ? AS foo FROM bar WHERE ? = 0
If we supply values of 'foo' for both placeholders, the query will actually be equivalent to a query containing two string literals:
SELECT 'foo' AS foo FROM bar WHERE 'foo' = 0
MySQL will run that statement, because it's a valid statement (if the table bar exists and we have privileges on it.) That query will return every row in bar (because the predicate in the WHERE clause evaluates to TRUE, independent of the contents of the table.. And we get returned the constant string foo.
It doesn't matter one whit that the string foo happens to match the name of column in our table.
This restriction has to do with how the SQL optimizer operates. We don't need to delve into all the details of the steps (briefly: parsing tokens, performing syntax check, performing semantics check, determining query plan, and then the actual execution of the query plan.)
So here's the short story: The values for bind parameters are supplied too late in that process. They are not supplied until that final step, the execution of the query plan.
The optimizer needs to know which tables and columns are being referenced at earlier stages... for the semantics check, and for developing a query plan. The tables and columns have to be identified to the optimizer. Bind placeholders are "unknowns" at the time the table names and column names are needed.
(That short story isn't entirely accurate; don't take all of that as gospel. But it does explain the reason that bind parameters can't be used for identifiers, like table names and column names.)
tl;dr
Given the particular statement you're running, the only value that can be passed in as a bind parameter would be the "hashedPassword" value. Everything else in that statement has to be in the SQL string.
For example, something like this would work:
String sqltext = "SELECT * FROM mytable WHERE mycolumn = ?";
PreparedStatement prepared = connection.prepareStatement(sqltext);
prepared.setString(1, hashedPassword);
To make other parts of the SQL statement "dynamic" (like the table name and column name) you'd have to handle that in the Java code (using string concatenation.) The contents of that string would need to end up like the contents of the sqltext string (in my example) when it's passed to the prepareStatement method.
The parameters of PreparedStatement should be applied only in parameters that can be used in conditional clauses. The table name is not the case here.
If you have a select where the table name can be applied in the conditional clause you can do it, otherwise you can not.

JDBC CallableStatement Stored Procedure CURSOR result set fetch

I want to export huge data from oracle to csv file. so i used simple JDBC select statement to get data in memory but and then write it to file, But data is very large of i am getting Out of memory exception. So i thought of using CallableStatement to call Stored Procedure which will return CURSOR with ResultSet as below :-
String getDBTableCursorSql = "{call getDBTableCursor(?,?)}";
callableStatement = dbConnection.prepareCall(getDBTableCursorSql);
callableStatement.setString(1, "test");
callableStatement.registerOutParameter(2, OracleTypes.CURSOR);
// execute getDBTableCursorSqlstore procedure
callableStatement.executeUpdate();
// get cursor and cast it to ResultSet
rs = (ResultSet) callableStatement.getObject(2);
// loop it like normal
while (rs.next()) {
String userid = rs.getString("ID");
String userName = rs.getString("NAME");
..
..
}
Oracle Proc :-
CREATE OR REPLACE PROCEDURE getDBTableCursor(
p_username IN DBUSER.USERNAME%TYPE,
c_dbuser OUT SYS_REFCURSOR)
IS
BEGIN
OPEN c_dbuser FOR
SELECT * FROM CUSTOMER WHERE USERNAME LIKE p_username || '%';
END;
Question 1 :-
does above ResultSet will fetch all the data in single shot ? or it will go to database for each rs.next(),
Question 2:-
is there any other approach which can deal with large data export to file in java using chunks so it wont get Out of memory issue?
I can't use pagination in this condition because of requirement.
Regarding your first question: the Oracle jdbc driver by default fetches 10 rows at a time. This can be verified or set to other value via standard jdbc:
https://docs.oracle.com/cd/E11882_01/java.112/e16548/resltset.htm#JJDBC28621

JDBC: Generating the value of primary key for prepared statements [duplicate]

This question already has answers here:
Is there a way to retrieve the autoincrement ID from a prepared statement
(5 answers)
Illegal operation on empty result set [duplicate]
(2 answers)
Closed 7 years ago.
Suppose I want to add a new row to my table via JDBC. In my table, I have an auto incrementing primary key field (so I can update the table later), and another regular field.
userid BIGINT AUTO_INCREMENT PRIMARY KEY,
username TEXT,
Now, I am creating the new statement and executing it using prepared statements, like so:
//dummy execute to get the generated keys
stmt.execute("SELECT * FROM user;", Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
int id=1;
//this is never executed, the resultset is always empty...
if(rs.next())
{
System.out.println("not empty");
id = rs.getInt(1);
}
System.out.println(id); //therefore, id is always 1
//prepare a statement to execute in SQL
stmt=con.prepareStatement("INSERT INTO user VALUES (?,?);", Statement.RETURN_GENERATED_KEYS);
//fill in the ?'s with their respective values
((PreparedStatement) stmt).setString(1, String.valueOf(id));
((PreparedStatement) stmt).setString(2, user);
//execute statement
((PreparedStatement) stmt).executeUpdate();
As you see, I want the value of the generated key so that I can use a prepared statement to update all the columns in the newly generated row (otherwise I get a No value specified for parameter 1 error).
But when I do the above code, I get an
Duplicate entry '1' for key 'PRIMARY'
This seems to me that the resultset is always empty. So I am not accessing the value correctly. Why is this so, and how can I fix this so that I can use my same structure of prepared statements to execute these queries?
You can call getGeneratedKeys only after you have executed you statement, not before. See https://docs.oracle.com/javase/8/docs/api/java/sql/Statement.html#getGeneratedKeys--
Simply preparing the statement does not generate the new key. Just drop the id column from your insert and insert only user.
1.Since your id is Auto Increment Column you should pass the value for that filed for first time (try to do it through MYSQL server directly.
2.Don't try to perform operations on empty result set.
3.In your case id always will be 1 since if statement doesn't execute.
Thank you.

Inserting variables with SQL in Java

I'm writing a webpage that takes input from a form, sends it through cgi to a java file, inserts the input into a database through sql and then prints out the database. I'm having trouble inserting into the database using variables though, and I was wondering if anyone would be able to help me out.
String a1Insert = (String)form.get("a1");
String a2Insert = (String)form.get("a2");
This is where I get my variables form the form (just believe that it works, there's a bunch more back end but I've used this before and I know it's getting the variables correctly).
String dbURL = "jdbc:derby://blah.blahblah.ca:CSE2014;user=blah;password=blarg";
Connection conn = DriverManager.getConnection(dbURL);
Statement stmt = conn.createStatement();
stmt.executeUpdate("set schema course");
stmt.executeUpdate("INSERT INTO MEMBER VALUES (a1Insert, a2Insert)");
stmt.close();
This is where I try to insert into the databse. It give me the error:
Column 'A1INSERT' is either not in any table in the FROM list or appears within a join specification and is outside the scope of the join specification or appears in a HAVING clause and is not in the GROUP BY list. If this is a CREATE or ALTER TABLE statement then 'A1INSERT' is not a column in the target table.
If anyone has any ideas that would be lovely ^.^ Thanks
java.sql.Statement doesn't support parameters, switching to java.sql.PreparedStatement will allow you to set parameters. Replace the parameter names in your SQL with ?, and call the setter methods on the prepared statement to assign a value to each parameter. This will look something like
String sql = "INSERT INTO MEMBER VALUES (?, ?)";
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, "a1");
stmt.setString(2, "a2");
stmt.executeUpdate();
That will execute the SQL
INSERT INTO MEMBER VALUES ('a1', 'a2')
Notice the parameter indexes start from 1, not 0. Also notice I didn't have to put quotes on the strings, the PreparedStatement did it for me.
Alternatively you could keep using Statement and create your SQL string in Java code, but that introduces the possibility of SQL injection attacks. Using PreparedStatement to set parameters avoids that issue by taking care of handling quotes for you; if it finds a quote in the parameter value it will escape it, so that it will not affect the SQL statement it is included in.
Oracle has a tutorial here.

Categories