PostgreSQL unique string field - java

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...
}

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')

Bogus data in ResultSet

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);
}

I want to insert employee id in address table(child table)-one to many concept

I am using Java Jdbc connection.
My question is very simple, I am inserted the employee record in a employee table
Ex:
Insert into employee (emp_id,emp_name) values(10,'hello');
How could I fetch the foreign key for child table(address) and insert it?
If I use select max(emp_id) from employee and Insert the id into child table
I'm afraid of if more than one person try to insert the values in employee table and there is a possibilities that I can get a wrong employee id for the child table.
Is there any possible way to insert the employee id in child table.
Note:I require a sql query for how to insert the employee id in child table
If you insert a record into a table that contains an AUTO_INCREMENT column, you can obtain the value stored into that column by calling the mysql_insert_id() function.
More details to refer this link
https://dev.mysql.com/doc/refman/5.0/en/getting-unique-id.html
You need to do a couple of things.
Assumed, your primary key in employee is not an AUTO_INCREMENT with the name emp_id and your inserting like Insert into employee (emp_id,emp_name) values(10,'hello'); you're done: the foreign key is 10.
BUT: if it is really an AUTO_INCREMENT, you must not specify any value for it:
Insert into employee (emp_name) values('Veronica');
You may then retrieve the last generated emp_id value by the driver or by a subsequent SQL statement. In JAVA, you need to tell the driver, that you want to have the last generated ID back:
PreparedStatement pst = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
Then you are able to fetch the generated primary key:
ResultSet rset = pst.getGeneratedKeys();
Long emp_id;
if (rset.next()) {
emp_id = rset.getLong(1);
}
Now Long emp_id contains the foreign key to insert in to address table.

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();

How can I insert new values into two related tables?

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().

Categories

Resources