java: retrieve keys after executeBatch() in H2 - java

I am trying to retrieve generated keys from an executeBatch() transaction but I only get the last key to be added.
this is my code:
PreparedStatement ps_insert = conn.prepareStatement(insertQuery, PreparedStatement.RETURN_GENERATED_KEYS);
for (int i = 0 ; i < adding_dates.length ; i++){
ps_insert.setInt(1, Integer.parseInt(consultant_id));
ps_insert.setDate(2, adding_dates[i]);
ps_insert.setInt(3, Integer.parseInt(room_id));
ps_insert.addBatch();
}
ps_insert.executeBatch();
ResultSet rs = ps_insert.getGeneratedKeys(); //<-- Only the last key retrieved
conn.commit();
What am I doing wrong?
EDIT: Apologies for not mentioning that I use H2 (http://www.h2database.com/html/main.html) database in embedded mode.

According to H2 jdbc driver javadocs, this is the normal behaviour:
Return a result set that contains the last generated auto-increment
key for this connection, if there was one. If no key was generated by
the last modification statement, then an empty result set is returned.
The returned result set only contains the data for the very last row.

You must iterate the ResultSet to retrieve the keys.
PreparedStatement ps_insert = conn.prepareStatement(insertQuery, PreparedStatement.RETURN_GENERATED_KEYS);
for (int i = 0 ; i < adding_dates.length ; i++){
ps_insert.setInt(1, Integer.parseInt(consultant_id));
ps_insert.setDate(2, adding_dates[i]);
ps_insert.setInt(3, Integer.parseInt(room_id));
ps_insert.addBatch();
}
ps_insert.executeBatch();
ResultSet rs = ps_insert.getGeneratedKeys(); //<-- Only the last key retrieved
if (rs.next()) {
ResultSetMetaData rsmd = rs.getMetaData();
int colCount = rsmd.getColumnCount();
do {
for (int i = 1; i <= colCount; i++) {
String key = rs.getString(i);
System.out.println("key " + i + "is " + key);
}
}
while (rs.next();)
}
conn.commit();

This is a limitation of H2 implementation. This is an issue.
For now use inserts/updates without batch, or query generated keys somehow through select.

If you are sharing a session/connection between 2 threads, and two of those threads try to execute statements at the same time, then you might see this kind of problem.
You probably need to either (a) use a connection pool or (b) synchronise your entire access to the DB.
for instance for option (b)
put a synchronize token infront of your method to make it thread safe
Just a thought as i dont know you complete execution context

Related

Cassandra: copy from Row to BoundStatement

I'm trying to write a generic tool for copying Cassandra table contents from one keyspace to another one (probably in a different cluster). All the tables are not too large.
Here is what I do:
Session source = ...
Session destination = ...
TableMetadata table = ...
final ResultSet rs = source.execute("select * from " + table.getName());
String insertCql = ...
PreparedStatement preparedStatement = destination.prepare(insertCql);
for (Row row : rs) {
final BoundStatement boundStatement = preparedStatement.bind();
for (int i = 0; i < rs.getColumnDefinitions().size(); i++) {
// bind column value from row to bountStatement
}
session.execute(boundStatement);
}
The problem is how to copy the column value from row to boundStatement. I can read it with row.getObject(i), but there is no corresponding setObject() in BoundStatement.
More precisely, this method exists in version 2.2 of the driver (it's cassandra-driver-dse), but that version does not work with Cassandra 3, and in version 3 of the driver (cassandra-driver-core) setObject() method does not exist. Instead, there are bunch of set() methods, all of them require Class, TypeToken or TypeCodec.
Where can I get those? ColumnDefinition only gives me DataType. It seems a doubtful idea to use row.getObject(i).getClass() to get Class.
Maybe there is a better approach to this task (schema-agnostic copying)?
I can look at DataType if the column and make a case per type to use setString() and so on, but this seems a bit overcomplicated and fragile.
You need to use bind variables in your insert statement and then bind the prepared statement with the column values from the result. Something along the lines of:
String insertCql = "INSERT INTO ks.tb (...) values (?,?,...)";
for (Row row : rs) {
List bindVariables = new ArrayList();
for (int i = 0; i < rs.getColumnDefinitions().size(); i++) {
bindVariables.add(rs.getObject(i));
}
final BoundStatement boundStatement = preparedStatement.bind(bindVariables.toArray(new Object[0]));
session.execute(boundStatement);
}

Java MySQL updating rows when deleting a row

I have a database with a AUTO_INCREMENTING id column and a name column. If I delete a row from the data I would like to update all of the other rows id so the sequence is right again, I am very lost please help me.
I tried this:
stmt = conn.createStatement();
rs = stmt.executeQuery("SELECT COUNT(id) FROM " + table_name_products);
int count = 0;
while(rs.next()){
count = rs.getInt("COUNT(id)");
}
stmt = conn.createStatement();
for(int i = 1; i <= count; i++){
if(i > id){
stmt.addBatch("UPDATE " + table_name_products + " SET id = "+ i-- +" WHERE id = "+ i +";");
}
}
I don't think this is possible.
I think the only solution would be to cache all rows after the one you are deleting some place, delete those rows, then add the cached rows to the table again.
There might be database management command to do this, but you wouldn't want to be doing it after every delete.
What about a database trigger. Have a look here.
A database trigger is procedural code that is automatically executed
in response to certain events on a particular table or view in a
database.

Resultset in java is not working according to my expectations

dbStatement = con.createStatement();
dbResult = dbStatement.executeQuery("SELECT Vendor_Code FROM temp ORDER BY status ASC ");
while (dbResult.next())
{
VendorCode=dbResult.getString("Vendor_Code");
System.out.println(VendorCode);
dbStatement.executeUpdate("INSERT INTO temp1 VALUES ('"+VendorCode+"')");
}
In the above code I am selecting a list of vendor code in the ascending order of their status,it works properly if I don't add:
dbStatement.executeUpdate("INSERT INTO temp1 VALUES ('"+VendorCode+"')");
into the while loop. If I add this I'm getting result as only the first value which is in the sorted list and also it's getting inserted into the temp1 table...
I am using Java swing and MySQL in NetBeans. Any idea please why this is happening?
If I do the above code in other way as,
dbStatement = con.createStatement();
dbInsert = con.createStatement();
dbResult = dbStatement.executeQuery("SELECT Vendor_Code FROM temp ORDER BY status ASC ");
while (dbResult.next())
{
VendorCode=dbResult.getString("Vendor_Code");
dbResult = dbInsert.executeQuery("SELECT Bid_No,Vendor_Name,Vendor_Address,Amount,Tax_Percentage,Amount_Aftertax,Expected_Deliverydate,Vendor_Code FROM purchase_bid where PE_Number='"+penumber+"' AND Vendor_Code='"+VendorCode+"' ");
while(dbResult.next())
{
Bid_Number=dbResult.getString("Bid_No");
vendor_name=dbResult.getString("Vendor_Name");
vendor_address=dbResult.getString("Vendor_Address");
Amount=dbResult.getString("Amount");
tax=dbResult.getString("Tax_Percentage");
date2=dbResult.getString("Expected_Deliverydate");
Amount_Aftertax=dbResult.getString("Amount_Aftertax");
venCode=dbResult.getString("Vendor_Code");
date3=date2.split("-");
String day="";
String month="";
String year="";
day=date3[2];
month=date3[1];
year=date3[0];
date=day+"-"+month+"-"+year;
addtoCart();//for displaying it in jTable
}
}
It does not take VendorCode in ascending order for retrieving values from purchase_bid table..Then it again takes first sorted value and nothing is displayed in jTable.
You can only use one result set per statement at a time (for a query, update etc.). See the doc:
By default, only one ResultSet object per Statement object can be open
at the same time
I would either:
load all your vendor codes into a collection, and then perform an update using a new statement/update
perform all the required actions within one SQL update statement (may be preferable for performance and transactional reasons. All the work will be contained in the database instance)
EDIT: Amended following EJP's comment below.
You should only use the statement instance for one operation. So calling the executeUpdate-method erases the ResultSet obtained by the executeQuery-method.
Create a second statement, so that:
dbStatement = con.createStatement();
dbInsert = con.createStatement();
dbResult = dbStatement.executeQuery("SELECT Vendor_Code FROM temp ORDER BY status ASC ");
while (dbResult.next())
{
VendorCode=dbResult.getString("Vendor_Code");
System.out.println(VendorCode);
dbInsert.executeUpdate("INSERT INTO temp1 VALUES ('"+VendorCode+"')");
}
If you're not just testing and you actually want to bulk insert the Vendor_Code from temp to temp1, try using INSERT ... SELECT
dbInsert = con.createStatement();
dbInsert.executeUpdate("INSERT INTO temp1 SELECT Vendor_Code FROM temp");
See the official MySQL documentation for further details on this type of INSERT:
http://dev.mysql.com/doc/refman/5.1/en/insert-select.html

Max value from auto commit false table

My problem is i have set a table auto commit false. I need to get the max id from that table(the currently inserted value of auto increment id). But i am getting the id of the previous committed process. Is it possible to get the value
My real problem is i need to insert into table some values, And need to take the id of the last inserted record from the first table and insert it to the second. The second insertion includes some image upload as well(as part of the code). so it take some delay or can have exceptions. I need to undo all insertions(both in the first and second) by occurring any exceptions. I tried to use commit-roll back method for this. But its not properly working as i mentioned above. main portion of my code is written below
try
{
//getting connection and setting auto commit false
dbHandler dbGetConnect=new dbHandler();
Connection conRegPlot=null;
conRegPlot=dbGetConnect.getconn();
conRegPlot.setAutoCommit(false);
String qryInsertPlot="INSERT INTO property_table(name,message,owner,locality,lattitude,longitude,country,state,city,type,catagory,created,modified,creted_date,zoompoint,mob_no,title,img_path,expire_date,lease_term) VALUES('none','"+description+"','"+sessionUserId+"','"+locality+"','"+lattitude+"','"+longitude+"','"+country+"','"+state+"','"+city+"','"+type+"','"+catagory+"',NOW(),NOW(),CURDATE(),'"+zoom+"','"+mob_no+"','"+title+"','NOT IN USE',"+expireDate+",'"+termsAndConditions+"')";//insertion into the first table
Statement stCrs=conRegPlot.createStatement();
int resp=stCrs.executeUpdate(qryInsertPlot);
String qryGetMaxProperty="SELECT MAX(l_id)"+
" FROM property_table"+
" WHERE stat='active'"+
" AND CURDATE()<=expire_date";
propertyId1=dbInsertPropert.returnSingleValue(qryGetMaxProperty);// get the max id
String qryInsertImage="INSERT INTO image_table(plot_id,ownr_id,created_date,created,modified,stat,img_path) VALUES('"+propertyId1+"','"+sessionUserId+"',CURDATE(),NOW(),NOW(),'active','"+img_pth+"')";
Statement stImg=conRegPlot.createStatement();
stImg.executeUpdate(qryInsertImage);// inserting the image
conRegPlot.commit();
}
catch(Exception exc)
{
conRegPlot.rollback();
}
finally{
conRegPlot.close();
}
Since
And need to take the id of the last inserted record from the first
table and insert it to the second.
You could the use of the new JDBC 3.0 method getGeneratedKeys() for get the generated ID. In ahother hand, you should use PreparedStatement for avoid SQL Injection.
PreparedStatement ps = null;
try {
conn = getConnection();
ps = conn.prepareStatement(myQuery, PreparedStatement.RETURN_GENERATED_KEYS);
ps.setInt(1, anInteger);
...
int rows = ps.executeUpdate();
if (rows == 1) {
ResultSet keysRS = ps.getGeneratedKeys();
if (keysRS.next())
int id = keysRS.getInt(1) // Get generated id
For MySQL, see more in http://dev.mysql.com/doc/refman/5.1/en/connector-j-usagenotes-last-insert-id.html#connector-j-examples-autoincrement-getgeneratedkeys

Calling Sybase Adaptive Server Enterprise's "sp_help" from JDBC

In order to query the database meta data in Sybase ASE, I found this relevant answer (not the accepted one), to be ideal:
From a Sybase Database, how I can get table description ( field names and types)?
Unfortunately, I can't seem to find any documentation, how I'm supposed to call sp_help from JDBC. According to the documentation, sp_help returns several cursors / result sets. The first one contains information about the table itself, the second one about the columns, etc. When I do this:
PreparedStatement stmt = getConnection().prepareStatement("sp_help 't_language'");
ResultSet rs = stmt.executeQuery();
while (rs.next()) {
System.out.println(rs.getObject(1));
// ...
}
I only get the results from the first cursor. How to access the other ones?
When you have multiple result sets you need to use the execute() method rather than executeQuery().
Here's an example:
CallableStatement cstmt;
ResultSet rs;
int i;
String s;
...
cstmt.execute(); // Call the stored procedure 1
rs = cstmt.getResultSet(); // Get the first result set 2
while (rs.next()) { // Position the cursor 3
i = rs.getInt(1); // Retrieve current result set value
System.out.println("Value from first result set = " + i);
// Print the value
}
cstmt.getMoreResults(); // Point to the second result set 4a
// and close the first result set
rs = cstmt.getResultSet(); // Get the second result set 4b
while (rs.next()) { // Position the cursor 4c
s = rs.getString(1); // Retrieve current result set value
System.out.println("Value from second result set = " + s);
// Print the value
}
rs.close(); // Close the result set
cstmt.close(); // Close the statement
You also need to call getUpdateCount() as well as getMoreResults() to read the entire result set. Here is some code I used to call sp_helpartition to retrieve partition information from a SYBASE DB.
try {
connection = getPooledConnection(poolName);
statement = connection.createStatement();
CallableStatement callable = connection.prepareCall(
"{ call sp_helpartition(?) }");
callable.setString(1,tableName);
callable.execute();
int partitions = 0;
/*
* Loop through results until there are no more result sets or
* or update counts to read. The number of partitions is recorded
* in the number of rows in the second result set.
*/
for (int index = 0 ; ; index ++){
if (callable.getMoreResults()){
ResultSet results = callable.getResultSet();
int count = 0 ;
while (results.next()){
count++;
}
if (index == 1){
partitions = count;
}
} else if (callable.getUpdateCount() == -1){
break ;
}
}
return partitions ;
} catch (Exception e) {
return 0 ;
} finally {
statement.close();
connection.close();
}
Thanks to Martin Clayton's answer here, I could figure out how to query Sybase ASE's sp_help function generically. I documented some more details about how this can be done in my blog here. I worked support for multiple JDBC result sets into jOOQ. In the case of sp_help, calling that function using the jOOQ API might look like this:
Factory create = new ASEFactory(connection);
// Get a list of tables, a list of user types, etc
List<Result<Record>> tables = create.fetchMany("sp_help");
// Get some information about the my_table table, its
// columns, keys, indexes, etc
List<Result<Record>> results = create.fetchMany("sp_help 'my_table'");

Categories

Resources