JDBC prepared statements/ refer to prepared in another prepared statements - java

I am new to JDBC and was a bit confused about the syntax. Say I wanted to create multiples queries ( or views in SQL) and refer to a view to make the subsequent queries, how do I do that with prepared statements?
For example:
PreparedStatement first = conn.PreparedStatement("SELECT * FROM clients);
first.execute()
Question Now can I pass the 'PreparedStatement' reference 'first' in the query I am about to create below?
PreparedStatement second = conn.PreparedStatement("SELECT * FROM first WHERE age > 10);

The easiest pattern for JDBC is:
try (PreparedStatement first = conn.prepareStatement(
"SELECT * FROM clients WHERE age BETWEEN ? AND ?")) {
first.setInt(1, 80);
first.setInt(2, 140);
try (ResultSet rs = first.executeQuery()) {
while (rs.next()) {
int age = rs.getInt("age");
...
}
}
}
This syntax: try-with-resources closes first and rs, even if you return or an exception is thrown.
And the statements: first and second are entirely independent.
One does not need to keep the prepared statements open for the entire database session.
The execution is best triggered by executeQuery for SELECT and executeUpdate for UPDATE/INSERT where no ResultSet is yielded.

Related

Can't reach temp table with using prepared statement for second time

I am using temporary tables inside my code in order to [some long sequnce of reasons here] in SQL Server, Java. I was executing my sql queries with using Stament object in java. However, recently I decided to use PreparedStatement in order to avoid injection thing.
My problem is when create a temporary table with using PreparedStatement, I can not reach it with the same prepared statement again. Here is a simple illustration:
sql = "select * into #someTable from (select someColumns from someOtherTable where smth = ? and smth2 = ?)"
PreparedStatement preparedStatement = conn.prepareStatement(sql);
for(int i=0; i<parameters.size(); i++){
preparedStatement.setString(i+1, parameters.get(i).toString());
}
this.rs = preparedStatement.executeQuery();
Until here, it is ok. After getting ResultSet and doing something with it, or without getting a resultSet just for preparedStatement.execute() does not makes difference, I can not reach the #someTable object again.
sql = "select count(*) from #someTable"
preparedStatement = conn.prepareStatement(sql);
this.rs = preparedStatement.executeQuery();
Here this.rs = preparedStatement.executeQuery(); part gives 'Invalid object name #someTable'. I am doing all of the things above with using one Connection object only and without closing or reopening it. I need to use that temp table again. Is there any way to create temp table with PreparedStatement object in java and reuse this temp table again and again? Regards,
Rather late to the party, but facing the same problem and finding the above answer wrong:
Read this article about the problem: https://learn.microsoft.com/en-us/sql/connect/jdbc/using-usefmtonly?view=sql-server-2017
I found that using a PreparedStatement to create the temp table wouldn't work, but if I changed to use a Statement to create the temp table it would work (even without the useFmtOnly).
So start with this (from the MS article) and build on it:
final String sql = "INSERT INTO #Bar VALUES (?)";
try (Connection c = DriverManager.getConnection(URL, USERNAME, PASSWORD)) {
try (Statement s = c.createStatement()) {
s.execute("CREATE TABLE #Bar(c1 int)");
}
try (PreparedStatement p1 = c.prepareStatement(sql); PreparedStatement p2 = c.prepareStatement(sql)) {
((SQLServerPreparedStatement) p1).setUseFmtOnly(true);
ParameterMetaData pmd1 = p1.getParameterMetaData();
System.out.println(pmd1.getParameterTypeName(1)); // prints int
ParameterMetaData pmd2 = p2.getParameterMetaData(); // throws exception, Invalid object name '#Bar'
}
}
The temp table you create in the first statement exists for the scope\lifetime of that request. As soon as you call another query, you're in a different scope so it is no longer present as it would have been cleaned up.
Solutions are either make 2 requests in the same call (not great) or create a global temp table that can be accessed by the second query (still not great).
The better solution is to create a stored procedure that does everything you need, with the temp table creation, querying and tidy up encapsulated in the procedure.
PS I can't see any surrounding code, but beware of SQL Injection when building queries in code like this.
Related info:
Scope of temporary tables in SQL Server

How to get all the rows affected by a UPDATE query in JDBC?

I have an assignment where I need to update records using a PreparedStatement. Once the record have been updated as we know update query return count, i.e., number of row affected.
However, instead of the count I want the rows that were affected by update query in response, or at least a list of id values for the rows that were affected.
This my update query.
UPDATE User_Information uInfo SET address = uInfo.contact_number || uInfo.address where uInfo.user_id between ? AND ?;
Normally it will return count of row affected but in my case query should return the ids of row or all the row affected.
I have used the returning function of PostgreSQL it is working but is not useful for me in that case.
i have used returning function of PostgreSQL but is not useful for me
It should be. Perhaps you were just using it wrong. This code works for me:
sql = "UPDATE table1 SET customer = customer || 'X' WHERE customer LIKE 'ba%' RETURNING id";
try (PreparedStatement s = conn.prepareStatement(sql)) {
s.execute(); // perform the UPDATE
try (ResultSet rs = s.getResultSet()) {
// loop through rows from the RETURNING clause
while (rs.next()) {
System.out.println(rs.getInt("id")); // print the "id" value of the updated row
}
}
}
The documentation indicates that we can also use RETURNING * if we want the ResultSet to include the entire updated row.
Update:
As #CraigRinger suggests in his comment, the PostgreSQL JDBC driver does actually support .getGeneratedKeys() for UPDATE statements too, so this code worked for me as well:
sql = "UPDATE table1 SET customer = customer || 'X' WHERE customer LIKE 'ba%'";
try (PreparedStatement s = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
s.execute(); // perform the UPDATE
try (ResultSet rs = s.getGeneratedKeys()) {
while (rs.next()) {
System.out.println(rs.getInt(1)); // print the "id" value of the updated row
}
}
}
Thanks, Craig!
You might be able to use JDBC's support for getting generated keys. See the Connection.prepareStatement(String sql, int[] columnIndexes) API method, then use Statement.getGeneratedKeys() to access the results.
The spec says "the driver will ignore the array if the SQL statement is not an INSERT statement" but I think PostgreSQL's JDBC driver will actually honour your request with other statement types too.
e.g.
PreparedStatement s = conn.prepareStatement(sql, new String[] {'id'})
s.executeUpdate();
ResultSet rs = s.getGeneratedKeys();
Otherwise, use RETURNING, as Gord Thompson describes.
There are two way of doing it
1. by passing an array of column name or index of column prepareStatement
i.e conn.prepareStatement(sql, new String[] {'id','uname'})
and
2. by using Statement.RETURN_GENERATED_KEYS in prepareStatement.
My code is for this i.e as per my requirement i have developed my code you can have a look for better idea.
private static final String UPDATE_USER_QUERY= "UPDATE User_Information uInfo SET address = uInfo.contact_number || uInfo.address where uInfo.user_id between ? AND ?;";
//pst = connection.prepareStatement(UPDATE_USER_QUERY,columnNames);
pst = connection.prepareStatement(UPDATE_USER_QUERY,Statement.RETURN_GENERATED_KEYS);
ResultSet rst = pst.getGeneratedKeys();
List<UserInformation> userInformationList = new ArrayList<UserInformation>();
UserInformation userInformation;
while (rst.next()){
userInformation = new UserInformation();
userInformation.setUserId(rst.getLong("user_id"));
userInformation.setUserName(rst.getString("user_name"));
userInformation.setUserLName(rst.getString("user_lName"));
userInformation.setAddress(rst.getString("address"));
userInformation.setContactNumber(rst.getLong("contact_number"));
userInformationList.add(userInformation);
}
That think i need to achieve in this case.
Hope so this will help you a lot.

Mysql configuration for using some caching

I use JDBC Java MySQL driver, but I found it is some how slow in cases when I run tests where I do drop table populate data and read it from straight after. data appears not there, But is I do delay for some second - data is there. I am afraid that not only Tests works this way and production code also may not read last data written.
So I need read just after written. without delay.
Would some caching help? Googled for and tried manuals still could not find answer.
here is some code i do use for inserting and retrieving:
private Connection conn = null;
private Statement stmt0;
private Statement stmt1;
#Inject
public DBConnectorMysql(#Named("JDBC_URL") String url, #Named("JDBC_CRED") String creds) throws SQLException {
conn = DriverManager.getConnection(url + creds);
stmt0 = conn.createStatement();
stmt1 = conn.createStatement();
}
#Override
public ResultSet executeQuery(String query) throws SQLException {
return stmt0.executeQuery(query);
}
#Override
public int executeUpdate(String query, String... values) throws SQLException {
PreparedStatement stmt = conn.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
int i = 0;
for (String s: values){
stmt.setString( ++i, s );
}
stmt.executeUpdate();
int newId = -1;
ResultSet rs = stmt.getGeneratedKeys();
if (rs.next()) {
newId = rs.getInt(1);
}
rs.close();
stmt.close();
return newId;
}
I do use Java SE with Mysql driver
I actively use three tables without foreign key.
From your code, it seems that you use multiple statement / prepared statement objects from the same connection that are open at the same time. This may have unexpected results (the JDBC spec doesn't really specify how this is handled, it's down to each vendor's implementation and I vaguelly recall a similar behaviour with an older version of MySQL/JDBC connector).
You should make sure that only one of them is open at any time. You could replace all your statements with a simple one, but as I see that's not practical since you want both Statement in executeQuery and a PreparedStatement in executeUpdate.
Otherwise you can move the createStatement calls inside the methods that use them. The best practice for executeQuery would be to return an ArrayList with your results (and close the ResultSet and stmt0 before returning).
Another, less elegant solution would be to write a closeQuery method that closes stmt0 after you're done with the ResultSet from executeQuery.
You have to call conn.commit() before rs.close() in order for your changes to take effect.

How to execute multiple SQL statements from java

I want to execute the multiple queries or job in one execute.
Something like this
eg:
String query="select * from tab1;insert into tab1 values(...);update tab1..;delete from tab1...;"
Statement st = con1.createStatement();
ResultSet rs = st.executeQuery(query);
Or multiple select queries.Queries will be dynamic.
But I am not able to do this.What is the way to run multiple queries separated by semi colon.
you can achieve that using Following example uses addBatch & executeBatch commands to execute multiple SQL commands simultaneously.
Batch Processing allows you to group related SQL statements into a batch and submit them with one call to the database. reference
When you send several SQL statements to the database at once, you reduce the amount of communication overhead, thereby improving performance.
JDBC drivers are not required to support this feature. You should use the DatabaseMetaData.supportsBatchUpdates() method to determine if the target database supports batch update processing. The method returns true if your JDBC driver supports this feature.
The addBatch() method of Statement, PreparedStatement, and CallableStatement is used to add individual statements to the batch. The executeBatch() is used to start the execution of all the statements grouped together.
The executeBatch() returns an array of integers, and each element of the array represents the update count for the respective update statement.
Just as you can add statements to a batch for processing, you can remove them with the clearBatch() method. This method removes all the statements you added with the addBatch() method. However, you cannot selectively choose which statement to remove.
EXAMPLE:
import java.sql.*;
public class jdbcConn {
public static void main(String[] args) throws Exception{
Class.forName("org.apache.derby.jdbc.ClientDriver");
Connection con = DriverManager.getConnection
("jdbc:derby://localhost:1527/testDb","name","pass");
Statement stmt = con.createStatement
(ResultSet.TYPE_SCROLL_SENSITIVE,
ResultSet.CONCUR_UPDATABLE);
String insertEmp1 = "insert into emp values
(10,'jay','trainee')";
String insertEmp2 = "insert into emp values
(11,'jayes','trainee')";
String insertEmp3 = "insert into emp values
(12,'shail','trainee')";
con.setAutoCommit(false);
stmt.addBatch(insertEmp1);//inserting Query in stmt
stmt.addBatch(insertEmp2);
stmt.addBatch(insertEmp3);
ResultSet rs = stmt.executeQuery("select * from emp");
rs.last();
System.out.println("rows before batch execution= "
+ rs.getRow());
stmt.executeBatch();
con.commit();
System.out.println("Batch executed");
rs = stmt.executeQuery("select * from emp");
rs.last();
System.out.println("rows after batch execution= "
+ rs.getRow());
}
}
refer http://www.tutorialspoint.com/javaexamples/jdbc_executebatch.htm
I'm not sure that you want to send two SELECT statements in one request statement because you may not be able to access both ResultSets. The database may only return the last result set.
Multiple ResultSets
However, if you're calling a stored procedure that you know can return multiple resultsets something like this will work
CallableStatement stmt = con.prepareCall(...);
try {
...
boolean results = stmt.execute();
while (results) {
ResultSet rs = stmt.getResultSet();
try {
while (rs.next()) {
// read the data
}
} finally {
try { rs.close(); } catch (Throwable ignore) {}
}
// are there anymore result sets?
results = stmt.getMoreResults();
}
} finally {
try { stmt.close(); } catch (Throwable ignore) {}
}
Multiple SQL Statements
If you're talking about multiple SQL statements and only one SELECT then your database should be able to support the one String of SQL. For example I have used something like this on Sybase
StringBuffer sql = new StringBuffer( "SET rowcount 100" );
sql.append( " SELECT * FROM tbl_books ..." );
sql.append( " SET rowcount 0" );
stmt = conn.prepareStatement( sql.toString() );
This will depend on the syntax supported by your database. In this example note the addtional spaces padding the statements so that there is white space between the staments.

Get last insert id with Oracle 11g using JDBC

I'm new to using Oracle so I'm going off what has already been previously answered in this SO question. I just can't seem to get it to work. Here's the statement that I'm using:
declare
lastId number;
begin
INSERT INTO "DB_OWNER"."FOO"
(ID, DEPARTMENT, BUSINESS)
VALUES (FOO_ID_SEQ.NEXTVAL, 'Database Management', 'Oracle')
RETURNING ID INTO lastId;
end;
When I call executeQuery the PreparedStatement that I have made, it inserts everything into the database just fine. However, I cannot seem to figure out how to retrieve the ID. The returned ResultSet object will not work for me. Calling
if(resultSet.next()) ...
yields a nasty SQLException that reads:
Cannot perform fetch on a PLSQL statement: next
How do I get that lastId? Obviously I'm doing it wrong.
make it a function that returns it to you (instead of a procedure). Or, have a procedure with an OUT parameter.
Not sure if this will work, since I've purged all of my computers of anything Oracle, but...
Change your declare to:
declare
lastId OUT number;
Switch your statement from a PreparedStatement to a CallableStatement by using prepareCall() on your connection. Then register the output parameter before your call, and read it after the update:
cstmt.registerOutParameter(1, java.sql.Types.NUMERIC);
cstmt.executeUpdate();
int x = cstmt.getInt(1);
I tried with Oracle driver v11.2.0.3.0 (since there are some bugs in 10.x and 11.1.x, see other blog). Following code works fine:
final String sql = "insert into TABLE(SOME_COL, OTHER_COL) values (?, ?)";
PreparedStatement ps = con.prepareStatement(sql, new String[] {"ID"});
ps.setLong(1, 264);
ps.setLong(2, 1);
int executeUpdate = ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
if (rs.next() ) {
// The generated id
long id = rs.getLong(1);
System.out.println("executeUpdate: " + executeUpdate + ", id: " + id);
}
When you prepare the statement set the second parameter to RETURN_GENERATED_KEYS. Then you should be able to get a ResultSet off the statement object.
You can use Statement.getGeneratedKeys() to do this. You just need to make sure to tell JDBC what columns you want back using one of the method overloads for that, such as the Connection.prepareStatement overload here:
Connection conn = ...
PreparedStatement pS = conn.prepareStatement(sql, new String[]{"id"});
pS.executeUpdate();
ResultSet rS = pS.getGeneratedKeys();
if (rS.next()) {
long id = rS.getLong("id");
...
}
You don't need to do the RETURNING x INTO stuff with this, just use the basic SQL statement you want.
Are you doing that in a stored procedure ? According to this Oracle document, it won't work with the server-side driver.
The Oracle server-side internal driver does not support
the retrieval of auto-generated keys feature.

Categories