I need to know how to execute an update in JDBC with Oracle database backend and retrieve values for a specific column of the records that have been updated. The column that I am interested in is part of a composite primary key, e.g. COL_NAME in the example below.
I have tried the following:
String query = "UPDATE T1 SET COL_ABC = 'A'"; // Simplified
statement = conn.prepareStatement(query.toString(), new String[] { "COL_NAME" });
ResultSet rs = statement.getResultSet();
while (rs.next()) {
rs.getLong("COL_NAME");
}
But statement comes back as null.
I am not sure how to utilize RETURNING INTO in this case unless converting this whole thing into an anonymous PL/SQL block, if it is indeed a possible solution.
Please note that I need a list of the values from this column from all the records that have been updated.
Related
I am a newbiee to Java database programming.
Recently during my project, I have got stuck in a concept, which I searched a lot but not getting any solution to satisfy my query, which may help me to get out of my problem.
Actually the problem is:
I have three table, let say one main (which contains common fields of actual data) table and two child table(which contains other different fields according to some criteria). Main table contain some part of information, and rest of information, depending on some criteria, will be saved in only one of the child table.
Now the scenario is like this, I have set autocommit off, then firing one insert query. So, when the insert query will be fired, database will give it a unique ID, in mysql, since the ID feild is autoIncrement. Now firing a Select Query, I want to extract that ID from main table. So, here is my question, Will SELECT QUERY BE ABLE TO EXTRACT THE ID OF THAT PARTICLULAR RECORD I HAVE JUST SAVED? Please remember that autocommit is set to false, and I have not committed yet.
I am doing this because I want that unique ID to be inserted in one of the child tables so that I can relate the information between table. So, After finding the ID, I have again fired a new INSERT query to save rest of the data in one of the child tables, now with the unique ID with rest of the data. And then on successful insertion, I have committed the connection.
Also, I want that either the information is saved in both (main and one of the child) tables or the details does not saves completely if any failure occur, so that I do not lose the partial information.
Please Help me in this. If you can explain what is relation between autocommit, savepoints. When to use, what things are to be remembered. Please provide some genuine resources, if you can, which demonstrate their nature,how they work under different circumstances, etc. I have googled but didn't got any such useful information. I want to get deep knowledge about it.
Thanks in advance :)
It looks like you want to get the ID when the record is added to the table. This is available when you insert the record if you use the getGeneratedKeys(). The autocommit cannot be used to return this.
The following code shows how this can be done.
/**
* Insert to database using JDBC 3.0 + which will return the key of the new row.
*
* #param sql is the sql insert to send to the database
* #return the key for the inserted row
* #throws DBSQLException
*/
public static int insertAndReturnKey(Connection dbConnection, String sql, List<SqlField> params) throws DBSQLException
{
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
String paramList = null;
try {
preparedStatement = dbConnection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
// Setup your parameters
int result = preparedStatement.executeUpdate();
resultSet = preparedStatement.getGeneratedKeys();
if (resultSet.next()) {
return (resultSet.getInt(1));
} else {
// throw an exception from here
throw new SQLException("Failed to get GeneratedKey for [" + sql + "]");
}
} catch (SQLException ex) {
throw new DBSQLException(buildErrorMessage(ex, sql, params));
} finally {
DBConnector.closeQuietly(preparedStatement);
DBConnector.closeQuietly(resultSet);
}
}
I am using an access mdb file as my database . Inside the table in my database I use id as autonumber. Now. I wanted to know how can i get the next generated auto number field in access database to put in java JTextField!
To get next number you can insert a "blank" row and use the number that was generated for it.
If your insert statement is stmt:
int nextKey = 0;
ResultSet keys = stmt.getGeneratedKeys();
if (keys.next())
{
nextKey = keys.getInt(1);
}
See statement.getGeneratedKeys()
Alternatively you could generate new id number with
SELECT MAX(id)+1 FROM yourTable
though this doesn't guarantee that the number will stay unused (by some other query) before you do anything with it.
Am creating a SQL Database for multiple users(Roughly 100 user), each records having nearly 15 fields in it.. In which the ID field is auto incremented...
Whenever a person Inserting a record to the database, it has to show "auto incremented ID" for that particular person, For this am using this code
PreparedStatement ppstmt = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
ppstmt.execute(sql,PreparedStatement.RETURN_GENERATED_KEYS);
ResultSet rs = ppstmt.getGeneratedKeys();
long key = 0;
if (rs != null && rs.next()) {
key = rs.getLong(1);
}
As of now its working fine but my doubt is when multiple users inserting the record at the same time, whether it will corresponding auto generated ID to each person..?
The statement will work correctly. The generated key returned will be the key generated by that execution of that statement for that user. These are SQL-defined semantics. Any implementation where it didn't work would be broken.
NB the result set cannot be null at the point you're testing it.
You have tagged oracle, so here is oracle's documentation on how retrieving generated keys works. A key piece of information is:
Auto-generated keys are implemented using the DML returning clause.
So it is worth looking at the documentation on how the returning clause works.
As you can see, this is guaranteed to return only data relevant to the just executed statement.
I would also like to point out that your use of a PreparedStatement is wrong. Once you have created the PreparedStatement from
PreparedStatement ppstmt = conn.prepareStatement(sql,PreparedStatement.RETURN_GENERATED_KEYS);
The next call should be to ppstmt.execute followed by ppstmt.getGeneratedKeys.
I have a store program in java and a database made in access. I already have 2 tables in my database which are the customers table and the products table.
I want to add an orders table wherein it's primary key is an autonumber and an order_line table to complete this app. I want to have tables like this..
customer(cust_id, name, ....)
orders(order_no, cust_id, date_purchased,...)
order_line(order_no, product_id, ...)
products(product_id, product_name, price,....)
When the customer purchased the products, i could insert new values to the orders table. The thing that is not clear to me is how could i insert also in the order_line table, because the order_no I created in access is of type autonumber.
Would I make a select statement first to get the order_no value to put it to the order_no in order_line's table? Or I need to put this in one query only.
Anyone with experience to this? Any advice is appreciated.
The insertion into orders and order_line table should happen in a single transaction. While doing so, if you are using plain JDBC to insert record into orders table, you can register the order_no as an OUT parameter in your CallableStatement and get the value after the statement is executed and use to set the order_no attribute on the order_line records.
// begin transaction
connection.setAutoCommit(false);
CallableStatement cs = connection.prepareCall(INSERT_STMT_INTO_ORDERS_TABLE);
cs.registerOutParameter(1, Types.INT);
int updateCount = cs.execute();
// Check the update count.
long orderNo = cs.getInt(1);
// CallableStatement csLine for inserting into order_line table
// for (OrderLine line: orderLines) {
// Set the orderNo in line.
// set paramters on csLine.
// csLine.addBatch();
// }
// run the batch and verify update counts
connection.commit();
// connection.rollback() on error.
The JDBC-way (if you like database-independence), is to use the getGeneratedKeys() method of statement.
Use setAutoCommit(false), then execute the first query with the option Statement.RETURN_GENERATED_KEYS (eg for PreparedStatement).
Then use the getGeneratedKeys() method to retrieve the key (note: reference by column name, as the exact implementation and number of returned columns depends on the driver implementation.
And execute the second statement with that retrieved key.
Finally, commit().
I have an auto generated timestamp that is created each time a record is inserted or updated in a mysql table. Is there a way to return this timestamp in a way similar to how I would use a keyholder to return a newly created id?
KeyHolder keyHolder = new GeneratedKeyHolder();
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
//Insert Contact
jdbcTemplate.update(new PreparedStatementCreator() {
#Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement preparedStatement = connection.prepareStatement(SQL_ADD, Statement.RETURN_GENERATED_KEYS);
preparedStatement.setString(1, contact.getFirstName());
preparedStatement.setString(2, contact.getLastName());
preparedStatement.setInt(3, contact.getOrganizationId());
preparedStatement.setString(4, contact.getType());
preparedStatement.setInt(5, contact.getUserId());
return preparedStatement;
}
}, keyHolder);
//Use keyholder to obtain newly created id
contact.setId(keyHolder.getKey().intValue());
Is there some way to also return the new timestamp without having to requery the table? I have been looking for ways to return it along with the id as a key in the keyholder, but it doesn't seem to be returned as a key?
Not very satisfying, but I think "no" is the answer to your question. I don't know any of the Spring stuff, but I think this is due to the basic JDBC that it's wrapping. See http://docs.oracle.com/javase/6/docs/api/java/sql/Statement.html#getGeneratedKeys%28%29
You only option would be to create a stored procedure on MySQL that has an out parameter and call that. See http://dev.mysql.com/doc/refman/5.0/en/call.html.
There are few options for solving this issue on the MySQL database server side. You could start with creating a TRIGGER on the table. As TRIGGER has a restriction and cannot return data, you can set the TIMESTAMP value to a variable:
DEMILITER //
CREATE TRIGGER ai_tbl_name AFTER INSERT ON tbl_name
FOR EACH ROW
BEGIN
SET #TimeStamp = NEW.timestamp_column;
END;//
DELIMITER ;
To retrieve this timestamp value, run the following command:
SELECT #TimeStamp;
Since the variables are stored in the memory, there will be no need to open any tables again.
You go even further. You could create a STORED PROCEDURE in MySQL to automate all the above (sample code, as I do not know your table's details):
DELIMITER //
DROP PROCEDURE IF EXISTS sp_procedure_name //
CREATE PROCEDURE sp_procedure_name (IN col1_val VARCHAR(25),
IN col2_val VARCHAR(25),
IN col3_val INT)
BEGIN
INSERT INTO tbl_name (col1, col2, col3)
VALUES (col1_val, col2_val, col3_val);
SELECT #TimeStamp;
END; //
DELIMITER ;
You can run this procedure with the following code:
CALL sp_procedure_name(col1_val, col2_val, col3_val);
As I'm not familiar with the Java, you'll need to finish it up with your side of code.
It seems that the variable contact is an instance for the newly inserted record. As it contains the newly generated id (primary key) field value, you can execute a new query to return the required timestamp field value for this new id.
The query may look like this:
select timestamp_field from my_table where id=?
Use PreparedStatement to input new id value and execute it to fetch required timestamp field value.
GeneratedKeyHolder also has two methods: getKeyList() that returns Map<String,Object> of generated fields; and getKeyList() that produces a list of generated keys for all affected rows.
See java doc of GeneratedKeyHolder and Spring tutorial of auto generated keys
In addition Spring's SimpleJdbcInsert has methods for generated key retrieval. See also method SimpleJdbcInsert#usingGeneratedKeyColumns
There are 2 methods in java.sql.Connection class causing PreparedStatement execution to return selected key columns :
PreparedStatement prepareStatement(String sql,
int[] columnIndexes)
throws SQLException
PreparedStatement prepareStatement(String sql,
String[] columnNames)
throws SQLException
You don't need to use Spring KeyHolder & JDBCTemplate to do this.
The give hope you could number/name your timestamp column. But the javadoc doesn't require or suggest that any JDBC implementation can return non-key columns, so your out of luck with this approach:
Creates a default PreparedStatement object capable of returning the auto-generated keys
designated by the given array. This array contains the names of the columns in the target
table that contain the auto-generated keys that should be returned.
As suggested in another answer, can switch to a stored procedure that does exactly what you want (CallableStatement is actually a PreparedStatement that executes storedprocedures - i.e. a subclass).
Can populate the timestamp column within the prepared statement via new Timestamp(new Date()) - but you should have in place a mechanism to sync times across your various servers (which is often used in windows and *nix environments). Your trigger could set the timestamp only if a value wasn't already provided.
As part of your app & DB design, you need to commit to a philosophy of where certain operations occur. If the DB derives needed data, the app needs to refresh data - you must pay the price of separate query executions or a combined stored proc that inserts & retrieves.