How to get a value from the last inserted row? [duplicate] - java

This question already has answers here:
How to get the insert ID in JDBC?
(14 answers)
Closed 7 years ago.
Is there some way to get a value from the last inserted row?
I am inserting a row where the PK will automatically increase, and I would like to get this PK. Only the PK is guaranteed to be unique in the table.
I am using Java with a JDBC and PostgreSQL.

With PostgreSQL you can do it via the RETURNING keyword:
PostgresSQL - RETURNING
INSERT INTO mytable( field_1, field_2,... )
VALUES ( value_1, value_2 ) RETURNING anyfield
It will return the value of "anyfield". "anyfield" may be a sequence or not.
To use it with JDBC, do:
ResultSet rs = statement.executeQuery("INSERT ... RETURNING ID");
rs.next();
rs.getInt(1);

See the API docs for java.sql.Statement.
Basically, when you call executeUpdate() or executeQuery(), use the Statement.RETURN_GENERATED_KEYS constant. You can then call getGeneratedKeys to get the auto-generated keys of all rows created by that execution. (Assuming your JDBC driver provides it.)
It goes something along the lines of this:
Statement stmt = conn.createStatement();
stmt.execute(sql, Statement.RETURN_GENERATED_KEYS);
ResultSet keyset = stmt.getGeneratedKeys();

If you're using JDBC 3.0, then you can get the value of the PK as soon as you inserted it.
Here's an article that talks about how : https://www.ibm.com/developerworks/java/library/j-jdbcnew/
Statement stmt = conn.createStatement();
// Obtain the generated key that results from the query.
stmt.executeUpdate("INSERT INTO authors " +
"(first_name, last_name) " +
"VALUES ('George', 'Orwell')",
Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
if ( rs.next() ) {
// Retrieve the auto generated key(s).
int key = rs.getInt(1);
}

Since PostgreSQL JDBC driver version 8.4-701 the PreparedStatement#getGeneratedKeys() is finally fully functional. We use it here almost one year in production to our full satisfaction.
In "plain JDBC" the PreparedStatement needs to be created as follows to make it to return the keys:
statement = connection.prepareStatement(SQL, Statement.RETURN_GENERATED_KEYS);
You can download the current JDBC driver version here (which is at the moment still 8.4-701).

The sequences in postgresql are transaction safe. So you can use the
currval(sequence)
Quote:
currval
Return the value most recently obtained by nextval for this sequence
in the current session. (An error is
reported if nextval has never been
called for this sequence in this
session.) Notice that because this is
returning a session-local value, it
gives a predictable answer even if
other sessions are executing nextval
meanwhile.

Here is how I solved it, based on the answers here:
Connection conn = ConnectToDB(); //ConnectToDB establishes a connection to the database.
String sql = "INSERT INTO \"TableName\"" +
"(\"Column1\", \"Column2\",\"Column3\",\"Column4\")" +
"VALUES ('value1',value2, 'value3', 'value4') RETURNING
\"TableName\".\"TableId\"";
PreparedStatement prpState = conn.prepareStatement(sql);
ResultSet rs = prpState.executeQuery();
if(rs.next()){
System.out.println(rs.getInt(1));
}

If you are using Statement, go for the following
//MY_NUMBER is the column name in the database
String generatedColumns[] = {"MY_NUMBER"};
Statement stmt = conn.createStatement();
//String sql holds the insert query
stmt.executeUpdate(sql, generatedColumns);
ResultSet rs = stmt.getGeneratedKeys();
// The generated id
if(rs.next())
long key = rs.getLong(1);
If you are using PreparedStatement, go for the following
String generatedColumns[] = {"MY_NUMBER"};
PreparedStatement pstmt = conn.prepareStatement(sql,generatedColumns);
pstmt.setString(1, "qwerty");
pstmt.execute();
ResultSet rs = pstmt.getGeneratedKeys();
if(rs.next())
long key = rs.getLong(1);

Use sequences in postgres for id columns:
INSERT mytable(myid) VALUES (nextval('MySequence'));
SELECT currval('MySequence');
currval will return the current value of the sequence in the same session.
(In MS SQL, you would use ##identity or SCOPE_IDENTITY())

PreparedStatement stmt = getConnection(PROJECTDB + 2)
.prepareStatement("INSERT INTO fonts (font_size) VALUES(?) RETURNING fonts.*");
stmt.setString(1, "986");
ResultSet res = stmt.executeQuery();
while (res.next()) {
System.out.println("Generated key: " + res.getLong(1));
System.out.println("Generated key: " + res.getInt(2));
System.out.println("Generated key: " + res.getInt(3));
}
stmt.close();

Don't use SELECT currval('MySequence') - the value gets incremented on inserts that fail.

For MyBatis 3.0.4 with Annotations and Postgresql driver 9.0-801.jdbc4 you define an interface method in your Mapper like
public interface ObjectiveMapper {
#Select("insert into objectives" +
" (code,title,description) values" +
" (#{code}, #{title}, #{description}) returning id")
int insert(Objective anObjective);
Note that #Select is used instead of #Insert.

for example:
Connection conn = null;
PreparedStatement sth = null;
ResultSet rs =null;
try {
conn = delegate.getConnection();
sth = conn.prepareStatement(INSERT_SQL);
sth.setString(1, pais.getNombre());
sth.executeUpdate();
rs=sth.getGeneratedKeys();
if(rs.next()){
Integer id = (Integer) rs.getInt(1);
pais.setId(id);
}
}
with ,Statement.RETURN_GENERATED_KEYS);" no found.

Use that simple code:
// Do your insert code
myDataBase.execSQL("INSERT INTO TABLE_NAME (FIELD_NAME1,FIELD_NAME2,...)VALUES (VALUE1,VALUE2,...)");
// Use the sqlite function "last_insert_rowid"
Cursor last_id_inserted = yourBD.rawQuery("SELECT last_insert_rowid()", null);
// Retrieve data from cursor.
last_id_inserted.moveToFirst(); // Don't forget that!
ultimo_id = last_id_inserted.getLong(0); // For Java, the result is returned on Long type (64)

If you are in a transaction you can use SELECT lastval() after an insert to get the last generated id.

Related

Can you help me to find the error below in the ResultSet rs=stmt....? [duplicate]

This question already has an answer here:
ResultSet is not for INSERT query? Error message: Type mismatch: cannot convert from int to String
(1 answer)
Closed 5 years ago.
I am getting a error on the resultset rs part where netbeans shows the error as
incompatible types:int cannot be converted to resultset
Class.forName("java.sql.Driver");
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql?useSSL=false", "root", "abc");
Statement stmt = conn.createStatement();
String query = "SELECT * FROM patient WHERE Mobile_No='" + mobno + "';"; /*Get the value from the database*/
ResultSet rs = stmt.executeUpdate(query);/*Part where the error is appearing*/
while (rs.next()) {
String Name = rs.getString("Name");
String Age = rs.getString("Age");
String Mobile = rs.getString("Mobile_No");
String gender = rs.getString("Gender");
String symptoms = rs.getString("Symptoms");
model.addRow(new Object[]{Name, Age, Mobile, gender, symptoms});
}
rs.close();
stmt.close();
conn.close();
Use stmt.executeQuery(String sql), it returns ResultSet.
If you want a ResultSet returned you should use executeQuery, not executeUpdate.
The stmt.executeUpdate(query); doesn't fit for an SELECT query.
You need to replace it by stmt.executeQuery(query);
Well the method executeUpdate returns a int not a results set, seen in the documentation here: https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#executeUpdate(java.lang.String)
the integer being return being either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements that return nothing
the method you are actually want to use is executeQuery and the documentation for that can be found at:
https://docs.oracle.com/javase/7/docs/api/java/sql/Statement.html#executeQuery(java.lang.String)
According the Javadoc (https://docs.oracle.com/javase/9/docs/api/java/sql/Statement.html#executeUpdate-java.lang.String-), stmt.executeUpdate(query); returns an int and not a ResultSet object.
From the Javadoc :
Returns:
either (1) the row count for SQL Data Manipulation Language (DML) statements or (2) 0 for SQL statements that return nothing
I think you must use stmt.executeQuery(query); instead, which return the ResultSet you expect. You're doing a SELECT and not an INSERT, UPDATE, or DELETE operation.
I believe people have already answered your question, which is statement.executeUpdate(query) returns the number of how many rows has been affected by executing the query, and you should use statement.executeQuery(query) instead ..
But this part String query = "SELECT * FROM patient WHERE Mobile_No = '" + mobno + "';" is very bad approach, it will leave the door opened for SQL injection, you should use PreparedStatement instead of Statement

Are ResultSet update{ColumnType} methods vulnerable to SQL injection?

A security scan made by AppScan source flags that the input has to be validated (Validation.Required) on the line uprs.updateString in the code below:
PreparedStatement statement =
conn.prepareStatement (query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
...
ResultSet uprs = statement.executeQuery ();
...
// Update DB ColumnA with input coming from client
uprs.updateString ('ColumnA', unvalidatedUserInput);
...
// Updates the underlying database
uprs.updateRow();
I assume that the intention behind this is to avoid SQL injection attacks, but I'm not sure whether that is possible in that scenario.
Questions: Are SQL Injection attacks possible through these JDBC methods? How does JDBC implements this under the scenes? Would this be another false positive reported by AppScan?
I'm not sure about bluemix-app-scan, but I'm providing my explanation here. (This is my assumption based on the below tests and code pasted)
I ran a test code to check this (in H2 DB)
value of testName String : (select 'sqlInjection' from dual)
Using createStatement (Not-Safe):
String query = "update TEST_TABLE set TEST_CHAR = " + testName + " where ID = 1";
Statement statement = connection.createStatement();
statement.executeUpdate(query);
Output: TEST_CHAR in DB was sqlInjection.
Using ResultSet of createStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet executeQuery = statement.executeQuery(query);
executeQuery.next();
executeQuery.updateString("TEST_CHAR", testName);
executeQuery.updateRow();
Output: Surprisingly TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using PreparedStatement (Safe):
String query = "update TEST_TABLE set TEST_CHAR = ? where ID = 1";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, testName);
statement.executeUpdate();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Using ResultSet of prepareStatement (Safe in H2 DB):
String query = "select * from TEST_TABLE where ID = 1";
PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = statement.executeQuery();
uprs.next();
uprs.updateString("TEST_CHAR", testName);
uprs.updateRow();
Output: Expected - TEST_CHAR in DB was (select 'sqlInjection' from dual).
Back to Questions:
Are SQL Injection attacks possible through these JDBC methods?
Maybe. It depends on the database driver that you're using.
How? :
The reason SQL Injection failed in my result set update was because H2 database internally uses PreparedStatement to update the row when ResultSet.updateRow() is invoked.
public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StatementBuilder buff = new StatementBuilder("UPDATE ");
...
buff.append(" SET ");
appendColumnList(buff, true);
...
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
...
for (int i = 0; i < columnCount; i++) {
...
v.set(prep, j++);
}
setKey(prep, j, current);
int count = prep.executeUpdate();
...
}
I'm not sure if all DB drivers in java implemented updateRow() method using preparedStatement or not. However it's clear that this is left to the driver and if bluemix is suggesting you to add a validation here, I suggest you follow that :)
How does JDBC implements this under the scenes?
Well, as shown above this is driver specific. However there is a good explanation on how PreparedStatement handles it over here.
Would this be another false positive reported by AppScan?
I don't think this is false positive (but in cases like H2 DB it is) but you'll never know if all database drivers implemented this securely.
Edit -
Even PostgreSQL and MySQL use PreparedStatement to handle this.
public synchronized void updateRow() throws SQLException
{
...
updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
...
updateStatement.executeUpdate();
...
}

Return id after sql insert java [duplicate]

Is there a way to retrieve the auto generated key from a DB query when using a java query with prepared statements.
For example, I know AutoGeneratedKeys can work as follows.
stmt = conn.createStatement();
stmt.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
if(returnLastInsertId) {
ResultSet rs = stmt.getGeneratedKeys();
rs.next();
auto_id = rs.getInt(1);
}
However. What if I want to do an insert with a prepared Statement.
String sql = "INSERT INTO table (column1, column2) values(?, ?)";
stmt = conn.prepareStatement(sql);
//this is an error
stmt.executeUpdate(Statement.RETURN_GENERATED_KEYS);
if(returnLastInsertId) {
//this is an error since the above is an error
ResultSet rs = stmt.getGeneratedKeys();
rs.next();
auto_id = rs.getInt(1);
}
Is there a way to do this that I don't know about. It seems from the javadoc that PreparedStatements can't return the Auto Generated ID.
Yes. See here. Section 7.1.9. Change your code to:
String sql = "INSERT INTO table (column1, column2) values(?, ?)";
stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
stmt.executeUpdate();
if(returnLastInsertId) {
ResultSet rs = stmt.getGeneratedKeys();
rs.next();
auto_id = rs.getInt(1);
}
There's a couple of ways, and it seems different jdbc drivers handles things a bit different, or not at all in some cases(some will only give you autogenerated primary keys, not other columns) but the basic forms are
stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
Or use this form:
String autogenColumns[] = {"column1","column2"};
stmt = conn.prepareStatement(sql, autogenColumns)
Yes, There is a way. I just found this hiding in the java doc.
They way is to pass the AutoGeneratedKeys id as follows
String sql = "INSERT INTO table (column1, column2) values(?, ?)";
stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
I'm one of those that surfed through a few threads looking for solution of this issue ... and finally get it to work. FOR THOSE USING jdbc:oracle:thin: with ojdbc6.jar PLEASE TAKE NOTE:
You can use either methods:
(Method 1)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
myPrepStatement = <Connection>.prepareStatement(yourSQL, Statement.RETURN_GENERATED_KEYS);
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
java.sql.RowId rid=rs.getRowId(1);
//what you get is only a RowId ref, try make use of it anyway U could think of
System.out.println(rid);
}
} catch (SQLException e) {
//
}
(Method 2)
Try{
String yourSQL="insert into Table1(Id,Col2,Col3) values(SEQ.nextval,?,?)";
//IMPORTANT: here's where other threads don tell U, you need to list ALL cols
//mentioned in your query in the array
myPrepStatement = <Connection>.prepareStatement(yourSQL, new String[]{"Id","Col2","Col3"});
myPrepStatement.setInt(1, 123);
myPrepStatement.setInt(2, 123);
myPrepStatement.executeUpdate();
ResultSet rs = getGeneratedKeys;
if(rs.next()) {
//In this exp, the autoKey val is in 1st col
int id=rs.getLong(1);
//now this's a real value of col Id
System.out.println(id);
}
} catch (SQLException e) {
//
}
Basically, try not used Method1 if you just want the value of SEQ.Nextval, b'cse it just return the RowID ref that you may cracked your head finding way to make use of it, which also don fit all data type you tried casting it to! This may works fine (return actual val) in MySQL, DB2 but not in Oracle.
AND, turn off your SQL Developer, Toad or any client which use the same login session to do INSERT when you're debugging. It MAY not affect you every time (debugging call) ... until you find your apps freeze without exception for some time. Yes ... halt without exception!
Connection connection=null;
int generatedkey=0;
PreparedStatement pstmt=connection.prepareStatement("Your insert query");
ResultSet rs=pstmt.getGeneratedKeys();
if (rs.next()) {
generatedkey=rs.getInt(1);
System.out.println("Auto Generated Primary Key " + generatedkey);
}

Getting resultset from insert statement

i have the below code, where I'm inserting records to a table. When I try to get resultset, it returns null. How to get the latest added row into a resultset?
String sql1 = "INSERT INTO [xxxx].[dbo].[xxxxxx](WORKFLOW_SEQ_NBR," +
" WORKFLOW_LOG_TYPE_CODE, WORKFLOW_STATUS_CODE, DISP_CODE, DISP_USER, DISP_COMMENT, DISP_TITLE, DISP_TS)" +
"VALUES(?,?,?,?,?,?,?,?)";
PreparedStatement pst = connect.prepareStatement(sql1);
pst.setString(1, ...);
pst.setString(2, ...);
...
...
...
pst.executeUpdate();
ResultSet rstest = pst.executeQuery();
// ResultSet rstest = pst.getResultSet();
EDIT: Resolved
added following method to go to the last added row
st.execute("Select * from [xxxx].[dbo].[xxxxxxxxx]");
ResultSet rstest = st.getResultSet();
rstest.afterLast();
GETLASTINSERTED:
while(rstest.previous()){
System.out.println(rstest.getObject(1));
break GETLASTINSERTED;//to read only the last row
}
When using a SQL statement such as INSERT, UPDATE or DELETE with a PreparedStatement, you must use executeUpdate, which will return the number of affeted rows. In this case there is simply no ResultSet produced by the sql operation and thus calling executeQuery will throw a SQLException.
If you actually need a ResultSet you must make another statement with a SELECT SQL operation.
See the javadoc for PreparedStatement#executeQuery and PreparedStatement#executeUpdate
Seems like this is an older question, but i'm looking for a similar solution, so maybe people will still need this.
If you're doing an insert statement, you can use the :
Connection.PreparedStatement(String, String[]) constructor, and assign those to a ResultSet with ps.getGeneratedKeys().
It would look something like this:
public void sqlQuery() {
PreparedStatement ps = null;
ResultSet rs = null;
Connection conn; //Assume this is a properly defined Connection
String sql = "insert whatever into whatever";
ps = conn.prepareStatement(sql, new String[]{"example"});
//do anything else you need to do with the preparedStatement
ps.execute;
rs = ps.getGeneratedKeys();
while(rs.next()){
//do whatever is needed with the ResultSet
}
ps.close();
rs.close();
}
Connection#prepareStatement() - Creates a PreparedStatement object for sending parameterized SQL statements to the database.
which means connect.prepareStatement(sql1); created the PreparedStatement object using your insert query.
and when you did pst.executeUpdate(); it will return the row count for SQL Data Manipulation Language (DML) statements or 0 for SQL statements that return nothing
Now if you again want to fetch the data inserted you need to create a new PreparedStatement object with Select query.
PreparedStatement pstmt = connect.prepareStatement("SELECT * FROM tableName");
then this shall give you the ResultSet object that contains the data produced by the query
ResultSet rstest = pstmt.executeQuery();

Value from last inserted row in DB [duplicate]

This question already has answers here:
How to get a value from the last inserted row? [duplicate]
(14 answers)
Closed 9 years ago.
Is there some way to get a value from the last inserted row?
I am inserting a row where the PK will automatically increase due to sequence created, and I would like to get this sequence number. Only the PK is guaranteed to be unique in the table.
I am using Java with a JDBC and Oracle.
I forgot to add that I would like to retrieve this value using the resultset below. (I have tried this with mysql and it worked successfully, but I had to switch over to Oracle and now I get a string representation of the ID and not the actually sequence number)
Statement stmt = conn.createStatement();
stmt.executeUpdate(insertCmd, Statement.RETURN_GENERATED_KEYS);
stmt.RETURN_GENERATED_KEYS;
ResultSet rs = stmt.getGeneratedKeys();
if(rs.next()){
log.info("Successful insert");
id = rs.getString(1);
}
The above snippet would return the column int value stored in a mysql table. But since I have switched over to Oracle, the value returned is now a strange string value.
What you're trying to do is take advantage of the RETURNING clause. Let's setup an example table and sequence:
CREATE TABLE "TEST"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(100 CHAR) NOT NULL ENABLE,
CONSTRAINT "PK_TEST" PRIMARY KEY ("ID")
);
CREATE SEQUENCE SEQ_TEST;
Now, your Java code should look like this:
String insertSql = "BEGIN INSERT INTO TEST (ID, NAME) VALUES (SEQ_TEST.NEXTVAL(), ?) RETURNING ID INTO ?; END;";
java.sql.CallableStatement stmt = conn.prepareCall(insertSql);
stmt.setString(1, "John Smith");
stmt.registerOutParameter(2, java.sql.Types.VARCHAR);
stmt.execute();
int id = stmt.getInt(2);
This is not consistent with other databases but, when using Oracle, getGeneratedKeys() returns the ROWID for the inserted row when using Statement.RETURN_GENERATEDKEYS. So you need to use the oracle.sql.ROWID proprietary type to "read" it:
Statement stmt = connection.createStatement();
stmt.executeUpdate(insertCmd, Statement.RETURN_GENERATED_KEYS);
ResultSet rs = stmt.getGeneratedKeys();
oracle.sql.ROWID rid = (oracle.sql.ROWID) rs.getObject(1);
But this won't give you the generated ID of the PK. When working with Oracle, you should either use the method executeUpdate(String sql, int[] columnIndexes) or executeUpdate(String sql, String[] columnNames) instead of executeUpdate(String sql, int autoGeneratedKeys) to get the generated sequence value. Something like this (adapt the value to match the index or the name of your primary key column):
stmt.executeUpdate(INSERT_SQL, new int[] {1});
ResultSet rs = stmt.getGeneratedKeys();
Or
stmt.executeUpdate(INSERT_SQL, new String[] {"ID"});
ResultSet rs = stmt.getGeneratedKeys();
While digging a bit more on this, it appears that this approach is shown in the Spring documentation (as mentioned here) so, well, I guess it can't be totally wrong. But, unfortunately, it is not really portable and it may not work on other platforms.
You should use ResultSet#getLong() instead. If in vain, try ResultSet#getRowId() and eventually cast it to oracle.sql.ROWID. If the returned hex string is actually the ID in hexadecimal flavor, then you can try converting it to decimal by Long#valueOf() or Integer#valueOf().
Long id = Long.valueOf(hexId, 16);
That said, Oracle's JDBC driver didn't support ResultSet#getGeneratedKeys() for a long time and is still somewhat troublesome with it. If you can't get that right, then you need to execute a SELECT CURRVAL(sequencename) on the same statement as you did the insert, or a new statement inside the same transaction, if it was a PreparedStatement. Basic example:
public void create(User user) throws SQLException {
Connection connection = null;
PreparedStatement preparedStatement = null;
Statement statement = null;
ResultSet generatedKeys = null;
try {
connection = daoFactory.getConnection();
preparedStatement = connection.prepareStatement(SQL_INSERT);
preparedStatement.setValue(1, user.getName());
// Set more values here.
int affectedRows = preparedStatement.executeUpdate();
if (affectedRows == 0) {
throw new SQLException("Creating user failed, no rows affected.");
}
statement = connection.createStatement();
generatedKeys = statement.executeQuery(SQL_CURRVAL);
if (generatedKeys.next()) {
user.setId(generatedKeys.getLong(1));
} else {
throw new SQLException("Creating user failed, no generated key obtained.");
}
} finally {
close(generatedKeys);
close(statement);
close(preparedStatement);
close(connection);
}
}
Oh, from your code example, the following line
stmt.RETURN_GENERATED_KEYS;
is entirely superfluous. Remove it.
You can find here another example which I posted before about getting the generated keys, it uses the normal getGeneratedKeys() approach.

Categories

Resources