Bogus data in ResultSet - java

I'm having some issues with my ResultSet using JDBC.
Here's my relation:
create table person (
person_id number(5) generated always as identity
minvalue 1
maxvalue 99999
increment by 1 start with 1
cycle
cache 10,
firstname varchar(10) not null,
lastname varchar(10) not null,
);
I'm trying to insert a (firstname, lastname) into the tuple and then get the person_id that comes out of it. Here's my JDBC code:
//connection is taken care of beforehand and is named con
prep = con.prepareStatement("insert into person (firstname, lastname) values (?, ?)", Statement.RETURN_GENERATED_KEYS);
prep.setString(1, firstname);
prep.setString(2, lastname);
prep.execute();
ResultSet generated = prep.getGeneratedKeys();
if (generated.next()) {
String key = generated.getString("0");
System.out.println(key);
}
This works all fine. But my problem is that the key should be an integer, not a String. Every time I run this, I get a ResultSet that contains a string of "AAA3vaAAGAAAFwbAAG", or something along those lines. I want to get the person_id so I can use it later in my Java program.
Is there something I'm doing wrong in regards to searching through the ResultSet or the execution of the statement itself?

tl;dr
int id = generated.getInt( 1 ) ;
Details
Your Question seems confused.
There are two forms of each get… method on ResultSet.
Pass a column number (an int)
Pass a column name (a String)
You seem to have combined the two into this:
String key = generated.getString( "0" ) ;
I doubt that you have a column named with a single digit zero. Besides being a poor choice of name, standard SQL forbids starting an identifier with a digit.
So that line makes no sense. Perhaps you meant the first column by using a zero 0 and mistakenly wrapped it in quotes, thereby transforming your intended int into an actual String.
Even that intention would be wrong. The ResultSet::getString documentation incorrectly describes the int as an “columnIndex”. Usually “index” means a zero-based counting offset. But actually ResultSet::getString( int ) requires you pass an ordinal number with counting starting at one. So getString( 0 ) is never valid.
So if you want to retrieve the value of your result set’s first column as text, do this:
String key = generated.getString( 1 ) ; // Retrieve first column of result set as text.
Yet again, this would be wrong in the context of your code. You are apparently attempting to retrieve the primary key values being generated during the INSERT. Your primary key column person_id is defined as number(5) which is not a textual type. So retrieving as a String is not appropriate.
NUMBER(5) is not standard SQL. If you happen to be using Oracle database, the doc says that would be an integer type with a precision of five, meaning numbers with up to five digits. So retrieve that as a integer type in Java by calling ResultSet::getInt.
int id = generated.getInt( 1 ) ; // Retrieve the new row’s ID from the first column of the result set of generated key values returned by the `INSERT` prepared statement.
My comments above are for databases in general. But for Oracle specifically, see the Answer by Mark Rotteveel explaining that Oracle database does not return the generated sequence number when calling getGeneratedKeys. Instead it returns ROWID pseudo-column.

Your problem is that Oracle by default returns the ROWID of the inserted record, and not the generated identifier. From Oracle JDBC Developer's Guide: Retrieval of Auto-Generated Keys:
If key columns are not explicitly indicated, then Oracle JDBC drivers
cannot identify which columns need to be retrieved. When a column name
or column index array is used, Oracle JDBC drivers can identify which
columns contain auto-generated keys that you want to retrieve.
However, when the Statement.RETURN_GENERATED_KEYS integer flag is
used, Oracle JDBC drivers cannot identify these columns. When the
integer flag is used to indicate that auto-generated keys are to be
returned, the ROWID pseudo column is returned as key. The ROWID
can be then fetched from the ResultSet object and can be used to
retrieve other columns.
So, if you use Statement.RETURN_GENERATED_KEYS, you'll get the ROWID, and you can then use that ROWID to select the inserted row to obtain the other values (including the generated identifier).
If you want to specifically retrieve the generated id, for Oracle you'll need to explicitly ask for that column as follows:
String[] columns = { "PERSON_ID" }
prep = con.prepareStatement(
"insert into person (firstname, lastname) values (?, ?)", columns);
prep.setString(1, firstname);
prep.setString(2, lastname);
prep.executeUpdate();
ResultSet generated = prep.getGeneratedKeys();
if (generated.next()) {
int key = generated.getInt("PERSON_ID");
System.out.println(key);
}

Related

How should my INSERT INTO statement for GENERATED BY DEFAULT AS IDENTITY be?

I'm trying to use GENERATED BY DEFAULT AS IDENTITY key for my record id's for my tables because a user needs to register themself and the user shouldn't be able to choose their own record id. So I decided to use GENERATED BY DEFAULT AS IDENTITY but I don't know how to write my INSERT statements.
This is my user table:
CREATE TABLE USER
(
ID_USER INT NOT NULL GENERATED BY DEFAULT AS IDENTITY,
USERNAME VARCHAR(20) UNIQUE NOT NULL,
FORENAME VARCHAR(20) NOT NULL,
SURNAME VARCHAR(20) NOT NULL,
PASSWORD VARCHAR(10) NOT NULL,
USER_TYPE INT NOT NULL,
PRIMARY KEY(ID_USER),
FOREIGN KEY (USER_TYPE) REFERENCES USER_TYPES(ID_TYPE)
);
and users will be allowed to register themselves.
This is what im using for my database
When a table has a column GENERATED BY DEFAULT AS IDENTITY it means that you can insert with a value in that column if you want to but you don't have to. So then in your insert, you could instead write
INSERT INTO ARTIST (ORIGIN,ARTIST_NAME) VALUES ('USA','Nirvana');
For reference: https://www.ibm.com/support/knowledgecenter/en/SSEPEK_11.0.0/apsg/src/tpc/db2z_identitycols.html
Edit after comment:
In the case where you need to retrieve the ID, #Mathias was correct that this is a duplicate question. A possible solution taken from this answer would be:
PreparedStatement result = cnx.prepareStatement(
"INSERT INTO ARTIST (ORIGIN,ARTIST_NAME) VALUES ('USA','Nirvana')",
RETURN_GENERATED_KEYS);
int updated = result.executeUpdate();
if (updated == 1) {
ResultSet generatedKeys = result.getGeneratedKeys();
if (generatedKeys.next()) {
int key = generatedKeys.getInt(1);
}
}
where key has the ID that you need for your next query.
The question is slightly different from the one already answered and needs a different answer.
The OP states: user shouldn't be able to choose their own record id. In that case the column definition should be ID_USER INT NOT NULL GENERATED ALWAYS AS IDENTITY to disallow any user-supplied value.
The table name shouldn't be USER as this is a reserved word. Try USERS instead.
The insert statement shouldn't insert into the ID_USER column. Similar to the example in the other answer, it should list the columns that are being inserted. An example below:
INSERT INTO USERS (USERNAME, FORENAME, SURNAME, PASSWORD, USER_TYPE)
VALUES ('JohnSmith','John', 'Smith', 'apasswrdx67', 3)
The OP wants to insert the generated value into another table using the GUI DatabaseManager. This is done by using the IDENTITY() function immediatly after inserting that row. For example,
INSERT INTO SOMETABLE (X, Y, Z) VALUES (IDENTITY(), 'some value', 'other value')

PostgreSQL unique string field

My database is PostgreSQL. The language is Java.
Table name is phrase with column name name.
At any time many users are inserting many rows to this table.
And we need to make sure that a certain field is unique.
And if such a field was found during loading, I want to return the row ID.
I could for example make a field the unique primary key, and when a row id inserted, catch the exception and look up the existing row.
But I think that is a bad idea.
I could just look for that row first and then insert.
But how can we avoid that the concurrent transactions get in each other's way?
And when downloading, is it better to do a batch download, and how do I do that in PostgreSQL? I do not even know.
You could create a UNIQUE constraint and INSERT ... ON CONFLICT:
CREATE TABLE mytable (
id integer PRIMARY KEY,
name text NOT NULL
CONSTRAINT name_unique UNIQUE
);
INSERT INTO mytable (id, name)
VALUES (1, 'me');
Now to run a batch INSERT that returns the id of each affecte row, run
INSERT INTO mytable (id, name)
VALUES (2, 'me'),
(3, 'new')
ON CONFLICT (name)
DO UPDATE SET name = EXCLUDED.name
RETURNING id;
The strange UPDATE that does not actually change the row is necessary if you want the id back.
Instead of catching the exception, you can use the INSERT ... ON CONFLICT DO NOTHING clause available in PostgreSQL. By checking the number of affected rows (returncode of PreparedStatement.executeUpdate), you can detect if there was a conflict.
E.g.
PreparedStatement pstmt = conn.prepareStatement("insert into x values (?,?) on conflict do nothing");
pstmt.setInt(1, myId);
pstmt.setInt(2, myValue);
int rc = pstmt.executeUpdate();
if (rc == 0) {
// fetch the existing row...
}

Retrieving Primary Key Generated By Trigger (Java)

Here's what I'm attempting to do. I have a Java program that decomms a telemetry stream into the individual raw fields. I am passing these raw fields values into a MySQL table where each column is one of the fields. In this database I also have a view that grabs all the telemetry data and calculates a few new derived columns based on raw data (e.g. raw counts to engineering units). In my Java program, after insertion I would like to grab the corresponding row from the VIEW (raw + derived) and pass that data along elsewhere.
Originally I thought I could simply insert the raw data into the VIEW and have the row returned to me in a ResultSet in the Java program. Unfortunately the data isn't returned in a ResultSet.
What I'm attempting to do now is insert the raw data into the table (this part works), get the primary key, then lookup the row from the VIEW. The part I'm struggling with is retrieving the primary key from the INSERT. I'm using a PreparedStatement generated from my Connection object and have supplied it with Statement.RETURN_GENERATED_KEYS. However, when I call getGeneratedKeys() on my statement after I call executeUpdate() my ResultSet is always empty. I can watch it insert the rows into the table while it's running...what am I doing wrong?
Can I not retrieve the generated primary key in this fashion if the primary key is generated via a trigger?
UPDATE: I've tried swapping out the Statement.RETURN_GENERATED_KEYS for a String array with the name of the primary key column, but that doesn't seem to work either.
I apologize for not including my code, but it would be difficult for me to do so. I've attempted to describe what I'm doing to the best of my abilities.
Ive seen this in another post. That claimed to work. Is this what yours looks like?
Statement stmt = db.prepareStatement(query, Statement.RETURN_GENERATED_KEYS);
numero = stmt.executeUpdate();
ResultSet rs = stmt.getGeneratedKeys();
if (rs.next()){
risultato=rs.getInt(1);
}
I have used SP's and here is an example of what I did. I used an in/out parm to get it back...
CREATE DEFINER=`scaha`#`localhost` PROCEDURE `updateprofile`(
IN in_idprofile INT(10),
IN in_usercode VARCHAR(50),
IN in_pwd VARCHAR(50),
IN in_nickname VARCHAR(50),
IN in_isactive tinyint,
IN in_updated timestamp,
OUT out_idprofile INT(10))
BEGIN
/* If the idprofile is < 1 then we insert a new record.. */
/* otherwise its an update */
if (in_idprofile < 1) then
insert into scaha.profile (usercode, pwd, nickname, isactive,updated) values (in_usercode,in_pwd,in_nickname,in_isactive,in_updated);
SET out_idprofile = LAST_INSERT_ID();
else
update scaha.profile set usercode = in_usercode, pwd = in_pwd, nickname = in_nickname, isactive = in_isactive, updated = in_updated
where idprofile = in_idprofile;
SET out_idprofile = in_idprofile;
end if;
END

Assign ID number automatically

strSQL = "INSERT INTO emp(NO, EMP_NAME, EMP_TEL)VALUES(088000, 'JIMMY', *****)";
stmt.executeUpdate(strSQL);
I have this statement to insert a new employee into the database.
What if I want the employee NO to be automatically generated by adding 1 to the previous employee NO? How can this be done in JSP?
While not JSP, a possible solution would be to create an auto generated incrementing column (known as an identity column) in the database. Importantly, this avoids the race condition that exists with a solution that retrieves the current maximum and increments it.
MySQL example:
create table emp (
emp_id integer not null auto_increment,
...
);
Apache Derby example:
create table emp (
emp_id integer not null generated always as identity,
...
);
MS SQL Server 2008 R2 example:
create table emp (
emp_id integer not null identity,
...
);
The INSERT statements do not include the emp_id column. See Statement.getGeneratedKeys() for obtaining generated id if required.
Depending of your DB... I give you a mysql example.
create table emp{
NO int unsigned auto_increment,
EMP_NAME varchar(30) not null,
...
}
insert into emp(EMP_NAME,...) values ("Jimmy", ...);
Now you can ask mysql the last inserted id with
LAST_INSERT_ID()
Yes of course, you can do this by setting "employee no" to be unique and A_I (auto_increament) in this column properties
Check database Schema where you are creating table emp with ID int NOT NULL AUTO_INCREMENT
Then update the schema strSQL = "INSERT INTO emp(EMP_NAME, EMP_TEL) VALUES('ABC_NAME', '321321')";
Though it is possible BUT we should not do any logical operation into JSP. Forward all input in Servlet and do there.
There are several way to do.
Some of databases like Oracle has features like sequence, which allows you to increment numbers sequently and operates as atomic.
Set the column (possibly primary key) to auto increment ( database option ), and do not specify that "NO" in your query. That way, the NO column you didn't add will be added by database automatically.
You can get max values from database table and add 1 for new NO, or you can save those latest value even in file, memcached, whatever you want. The problem of this #3 is, if you don't make program to be atomic between GET LATEST VALUE, ADD 1, CALL DATABASE INSERT QUERY, multiple query can have same NO to use. It's OK, however, if NO is primary key since only very first update/insert query will executed and others query will be failed due to primary key unique violation... but problematic in some cases.
You can use the AUTOINCREMENT option on the field NO on the database, or execute a query like SELECT MAX(NO) FROM emp
and get the max value
I think this will be going to solve your doubt in database and use this following query as:
CREATE TABLE:
CREATE TABLE `test` (
`id` INT(5) UNSIGNED NOT NULL AUTO_INCREMENT,
`emp_name` VARCHAR(50) NOT NULL,
`emp_tel` INT(5) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
INSERT TABLE METHOD:1
INSERT INTO test
VALUES (0,jmail,1234567)OR(?,?,?);
INSERT TABLE METHOD:2
INSERT INTO test (id,emp_name,emp_tel)
VALUES (0,jmail,1234567);
If you had any doubt give me comment.
And if your using the sqlyog to use the shortcut.
if your wants this method like following as:
PreparedStatement ps = con.prepareStatement("INSERT INTO test(id,emp_name,emp_tel)
VALUES (0,jmail,1234567)");
ps.executeUpdate();
PreparedStatement ps = con.prepareStatement("INSERT INTO test(id,emp_name,emp_tel)
VALUES (?,?,?)");
ps.setString(1, id );
ps.setString(2, name);
ps.setString(3, tel);
ps.executeUpdate();

What is the standard practice for extracting an auto-incrementing primary key?

I have some objects that will be created an entered into the database. There is no guarantee that any of their fields will be unique except for the auto-incrementing primary key. I would like to be able to access these objects later in the program using their primary key.
What is the best way to get this key out of the database? I can think of two methods to obtain the primary key after adding the object's data to the database:
Find the maximum primary key as this was the one most recently
added.
Delete the current object, compare the data in the database with the data in the program, and then create a new object based on the row that is not contained in the program.
Both of these seem error-prone and hacky and I am wondering if there was a more standard way do to this.
Most databases have a way to access the last generated ID, take SQL server for example.
CREATE TABLE [SomeTable] (
ID INT NOT NULL IDENTITY(1,1),
FieldOne VARCHAR(MAX) NOT NULL,
PRIMARY KEY(ID)
)
INSERT INTO [SomeTable](FieldOne) VALUES('a')
SELECT scope_identity() -- returns 1
INSERT INTO [SomeTable](FieldOne) VALUES('b')
SELECT scope_identity() -- returns 2
This has the advantage of being safe for multiple users / transactions.
The first suggestion about guessing what the next ID would be is not safe in that regard.
Your second suggestion will wreck havoc if you have any referential integrity or if there are any triggers.
Take a look at
java.sql.Statement.executeUpdate(String sql, int autoGeneratedKeys)
Executes the given SQL statement and signals the driver with the given flag about whether the auto-generated keys produced by this Statement object should be made available for retrieval.
Java has defined methods to find auto generated key values irrespective of the SQL you are using. Following is an example on how to use Java code to find them.
Use getGeneratedKeys() method from your Statement or PreparedStatement object to identify the new auto generated values. Iterate the returned ResultSet object to get the newly generated key values in the order of batch statements.
This call may throw java.sql.SQLFeatureNotSupportedException if the JDBC driver, that you are using, does not support this method.
Sample code snippet:
String sql_insert =
"insert into my_table( non_auto_incrmnt_fld_names_,_separated ) " +
" values ( record1-values,cs-foreach-field ), "
" ( record2-values,cs-foreach-field )"; // append as many as required
...
int rowsAffected =
stmtObject.executeUpdate( sql_insert, Statement.RETURN_GENERATED_KEYS );
ResultSet rs = stmtObject.getGeneratedKeys();
//******************************************************
rs.last();
int rows = rs.getRow();
System.out.println( "Generated keys count: " + rows );
//******************************************************/
int currentRow = 1;
rs.beforeFirst();
while( rs.next() ) {
System.out.println( /**/( currentRow++ ) + " = " + /**/rs.getInt( 1 ) );
} // while rs

Categories

Resources