is there any way of retrieving database generated values in Java other than IDENTITY fields? I can easily get IDENTITY values from a ResultSet, but I'd like to get the value of a date field which has been generated by the database (CURRENT_TIMESTAMP). I prefer not to send another SELECT query to get the date.
statement = connection.prepareStatement("INSERT INTO foo (bar_date) VALUES (CURRENT_TIMESTAMP)");
ResultSet generatedKey = statement.getGeneratedKeys();
while (generatedKey.next()) {
// read the key..., this unfortunately only returns IDENTITY columns.
}
Have you tried something like
INSERT INTO foo (bar_date)
OUTPUT INSERTED.bar_date
VALUES (CURRENT_TIMESTAMP)
Have a look at OUTPUT Clause (Transact-SQL)
Assuming you're using SQL 2005 or later, you could add an OUTPUT clause to your insert statement to have it return a result set.
I'm not sure of the Java syntax to retrieve the resultset, but it would be something like the following:
statement = connection.prepareStatement("INSERT INTO foo OUTPUT inserted.id, inserted.bar_date (bar_date) VALUES (CURRENT_TIMESTAMP)"); //guessing the name of the id column
ResultSet returnSet = statement.execute(); // or however you do this
while (resultset.next()) {
// resultset will have two columns - id and bar_date
}
Related
String sql = "Select MAX(ORDERLINEID) From ORDERLINESTABLE";
ResultSet rst;
rst = stmt.executeQuery(sql);
if(rst.next())
{
next = rst.getInt("ORDERLINEID");
next++;
}
I have a table called ORDERLINESTABLE in my database which is currently empty. I have run the above code with the aim is to get the highest integer stored in the ORDERLINEID column allowing me to increment it when adding items to the database.
I expected this query to return nothing as the table is empty but when debugging I noticed that the search is returning true for the rst.next() method.
Does anyone have any idea why this would be? I have looked at the resultset.next() documentation and as far as I can see it should return false.
When in doubt, look at your data. Here is a sample query from any db engine.
select max(field) maxValue
from table
where 1=3
It will yield
maxValue
Null
In other words, your query is returning one record with a value of null.
It is much better to fetch the ORDERLINEID filled in by the database after the INSERT statement. Make the column ORDERLINEID of type INT AUTOINCREMENT.
String sql = "INSERT INTO ORDERLINESTABLE(xxx, yyy) VALUES (?, ?)";
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
stmt.setString(1, xxx);
stmt.setInt(2, yyy);
int updateCount = stmt.executeUpdate(); // 1
try (ResultSet id = stmt.getGeneratedKeys()) {
if (id.next()) { // 'if' as just 1 row inserted.
int orderLineId = id.getInt(1); // 1 key per row.
}
}
}
Java has a database independent way to fetch the generated keys of an INSERT. That is a lot safer than taking the MAX afterwards or before, in a multi-user environment.
Scenarios for wrong IDs are numerous in a multiuser environment:
first SELECT
second SELECT
second increment for new ID
first increment for new ID
first INSERT
second INSERT
I have a derby users database which I query, when the user clicks login on the application.
However, when I query the users table with the parameter [user] derby returns a null Object instead of the record it ought to return.
Here is my code:
String ssql = "SELECT * FROM USERS WHERE UNAME LIKE ?";
try{
DriverManager.registerDriver(new org.apache.derby.jdbc.EmbeddedDriver());
con = DriverManager.getConnection(url);
sql = con.prepareStatement(ssql, Statement.RETURN_GENERATED_KEYS);
sql.setString(1, cbox_chooseUser.getSelectedItem().toString());
sql.executeQuery();
ResultSet rs = sql.getGeneratedKeys();
try{
while (rs.next()) {
if(rs.getString("PW").toCharArray().equals(txt_password.getPassword())){
sql.close();
con.close();
return true;
}
} catch (NPE ...) {...}
}
I tried it multiple times wit a test user with both the pw and the username set to "test"; but I always get the same error.
Why is the recordset always Null?
Thanks for your help :)
The documentation says
ResultSet getGeneratedKeys() throws SQLException
Retrieves any auto-generated keys created as a result of executing this Statement
object.
If this Statement object did not generate any keys, an empty
ResultSet object is returned.
Your select statement isn't generating any keys that's why it's returning an empty ResultSet. You aren't inserting anything hence no keys are being generated.
You can try ResultSet rs = sql.executeQuery();. It should work.
You are using it in wrong way.
The generated keys concept should be used only in the case DML of insert type query but not in the case of select query.
select simply select the rows from the table. In this case there is no chance of any keys getting generated.
In the case of insert query if any column is configured as auto increment or kind of functionality then some keys will get generated. These keys can be caught using Statement.RETURN_GENERATED_KEYS in java.
As you are using select query there is no need of using Statement.RETURN_GENERATED_KEYS.
You just modify below lines and everything will be fine.
sql = con.prepareStatement(ssql, Statement.RETURN_GENERATED_KEYS);
sql.setString(1, cbox_chooseUser.getSelectedItem().toString());
sql.executeQuery();
ResultSet rs = sql.getGeneratedKeys();
with
sql = con.prepareStatement( ssql );
sql.setString( 1, cbox_chooseUser.getSelectedItem().toString() );
ResultSet rs = sql.executeQuery();
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.
My source code has the following structure:
SourceFolder
AddProduct.jsp
Source Packages
-Controller(Servlets)
SaveProduct.java
-Model(Db Operations)
ProductDbOperations.java
I am inserting a new product into the product table and at the same time I am inserting an entry into product_collection table (product_id | collection_id).
To insert an entry into the product_collection table i need to get generated id from product table. After that a new entry is inserted into the product_collection table.
Also, I am not using any Framework and am using Netbeans 7.3.
Problem:
A new entry is inserted into the product table with this piece of code
IN: ProductDbOperations.java
try
{
this.initConnection(); // Db connection
pst = cn.prepareStatement("INSERT INTO product values('"+name+"', "+quantity+", "+price+")");
rs = pst.executeUpdate();
}
catch(SQLException ex)
{
}
I Also used the solution at following link which doesn't works for me.
I didn't got any SQL exception
How to get the insert ID in JDBC?
so help me find out why this code not working for me .
Thanks a million.
Not all drivers support the version of getGeneratedKeys() as shown in the linked answer. But when preparing the statement, you can also pass the list of columns that should be returned instead of the "flag" Statement.RETURN_GENERATED_KEYS (and passing the column names works more reliably in my experience)
Additionally: as javaBeginner pointed out correctly, your usage of a prepared statement is wrong. The way you do it, will still leave you wide open to SQL injection.
// run the INSERT
String sql = "INSERT INTO product values(?,?,?)";
pst = cn.prepareStatement(sql, new String[] {"PRODUCT_ID"} );
pst.setString(1, name);
pst.setInt(2, quantity);
pst.setInt(3, price);
pst.executeUpdate();
// now get the ID:
ResultSet rs = pst.getGeneratedKeys();
if (rs.next()) {
long productId = rs.getLong(1);
}
Note that the column name passed to the call is case-sensitive. For Oracle the column names are usually uppercase. If you are using e.g. Postgres you would most probably need to pass new String[] {"product_id"}
The way you are using is not the proper way of using preparedstatement
use the following way
pst = cn.prepareStatement("INSERT INTO product values(?,?,?)");
pst.setString(1,name);
pst.setInt(2,quantity);
pst.setInt(3,price);
pst.executeUpdate();
Yes there is a way to retrieve the key inserted by SQL. You can do it by:
Using Statement.RETURN_GENERATED_KEYS in your previous insert and get the key which can be used in further insert
e.g:
String query = "INSERT INTO Table (Col2, Col3) VALUES ('S', 50)";
Statement stmt = con.createStatement();
int count = stmt.executeUpdate(query, Statement.RETURN_GENERATED_KEYS);
Here is the code that works:
Connection c = ds.getConnection();
c.setAutoCommit(false);
PreparedStatement stmt = c.prepareStatement("INSERT INTO items (name, description) VALUES(?, ?)");
while (!(items = bus.take()).isEmpty()) {
for (Item item : items) {
stmt.setString(1, item.name);
stmt.setString(2, item.description);
stmt.addBatch();
}
stmt.executeBatch();
c.commit();
}
But now I need to populate another table where id is a foreign key.
If I use INSERT with RETURNING id then executeBatch fails with "A result was returned when none was expected" error.
I see several ways to solve this
Do individual insert rather than the batch insert.
Replace serial id with client generated guid.
Use some kind of a stored procedure to perform the batch insert and return a list of ids.
Of the three methods that I see the last one seems to preserve both the efficiency of batch insert and return the ids, but it is also the most complex for me as I have never written stored procedures.
Is there a better way to batch insert and get the IDs? I have no problem using postgresql specific API rather than jdbc.
If not, could any one sketch such a stored procedure?
Here is the table schema:
CREATE UNLOGGED TABLE items
(
id serial,
name character varying(1000),
description character varying(10000)
)
WITH (
OIDS=FALSE
);
Something like this should work:
// tell the driver you want the generated keys
stmt = c.prepareStatement("INSERT ... ", Statement.RETURN_GENERATED_KEYS);
stmt.executeBatch();
// now retrieve the generated keys
ResultSet rs = stmt.getGeneratedKeys();
while (rs.next()) {
int id = rs.getInt(1);
.. save the id somewhere or update the items list
}
I think (I am not sure!) that the keys are returned in the order they were generated. So the first row from the ResultSet should map to the first "item" from the list you are processing. But do verify that!
Edit
If that doesn't work, try specifying the actual columns for which the values are generated:
stmt = c.prepareStatement("INSERT ... ", new String[] {"id"});