Performatic insert into database to catch duplicate key error - java

My application can occur to insert duplicate primary key data, my doubt is what would be the most performative way, check before inserting if the key already exists or simply insert and let the bank return an error and then treat it? Which has less impact?
I thank

The most performant method would be to use an INSERT query with a ON DUPLICATE KEY UPDATE clause.
This will insert a new record if the primary key doesn't exist and will update only the specified fields for the primary key record if it already exists (it will not throw an error).
For example the following code will create a new user if the the user_id doesn't exist. If the user_id does exist, it will only update the last_visited_date.
INSERT INTO users
(user_id, last_visited_date)
VALUES
({user_id}, UTC_TIMESTAMP())
ON DUPLICATE KEY UPDATE
last_visited_date= VALUES(last_visited_date)
This approach allows you to both create new records and update already existing records with one query, which prevents the need for checking for an already existing primary key.
Note: if the primary key is an auto-incremented index, MySQL treats ON DUPLICATE UPDATE clauses the same as INSERT queries so the next ID will be incremented by 1 anytime the primary key already exists.
If you don't want to use an ON DUPLICATE KEY UPDATE clause, catching the error for already existing primary keys would be the most performant.

Related

Auto Increment in primary Key evenThough there is Constrain voilation Exception Mysql(5.6.34)

I have created a table User with following details
{
id , //autoincrment
name
}
and Table Account which has a User with foreign key relation
Account {
id , //autoincrement
userid //foreign key reference to user
}
when i insert in to account with value userId which doesnot exist in user table
i get Constraint voilation Exception
Cannot add or update a child row: a foreign key constraint fails (`schema`.`account`, CONSTRAINT `constraint_name` FOREIGN KEY (`user_id`) REFERENCES `User` (`id`))
but my AUTO increment Count for Account got increased.
what i meant to say is before exception account table has max id of 100.
when i insert new record it fails with above described exception.i corrected query and insert with correct userid then i see accountid value as 102.
is it a correct behaviour from mysql.should it increment autoincrement value even though query execution fails?
It's intended and correct, that auto_increment works outside the scope of transactions (like sequences in other database systems like oracle do as well). The reason is that two different transactions must not draw the same number, and a locking on such a neuralgic point as the key column of a table would have enormous performance impact in concurrent situations otherwise.
Cf. also this SO answer.

Primary Key violation

The below insert statement is not working saying:unique primary key violated ora-0001. Do you know why it is not working ?
primary key is the seq id
INSERT INTO VITA_TRANSACTION ( VTR_SEQ_ID,VTR_QUELLE_SEQ_ID,VTR_QUELLE,
VTR_PROVIS_EXPORT_LOG_ID,VTR_DWH_EXPORT_LOG_ID,VTR_SYS_DATE,
VTR_PROCESSSTATE,VTR_IMEI_NR,VTR_IMEI_ZUORDNUNG,VTR_ZUORDNUNG_DATE,
VTR_IMEI_KNZ,VTR_SUBSCRIBER_NO,VTR_INSERT_TYPE,VTR_ERFASSUNG_DATUM,
VTR_VOID_ACTIVATE,VTR_QUELLSYSTEM,VTR_VORGANG,VTR_STORNO,VTR_SALES_PRICE,
VTR_ARTICLE_NO)
values (SEQ_VITA_TRANSACTION.nextval,418912,'M-ABVK',null,null,sysdate,
'UM','352523003062648','352523003062648',
to_date('20160118194708', 'YYYYMMDDhh24miss'),null,32927785,'AK',
to_date('20160118000000', 'YYYYMMDDhh24miss'),'60000661','Activate',
'Act','N',2000,'123123')
PK violation means that your table already contains the id that your sequence is creating. Try check the value of SEQ_VITA_TRANSACTION.nextval and the max(VTR_SEQ_ID) from VITA_TRANSACTION:
select 'seq', SEQ_VITA_TRANSACTION.nextval from dual
union all
select 'tab', max(VTR_SEQ_ID) from VITA_TRANSACTION
This may not the exact answer to your question but I am trying to debug your problem.
ORA-0000
1: unique constraint (string.string) violated
Cause: An UPDATE or INSERT statement attempted to insert a duplicate key. For Trusted Oracle configured in DBMS MAC mode, you may see this message if a duplicate entry exists at a different level.
So duplicate entry already exists in VITA_TRANSACTION table.
First you should retrieve the current value of the sequence without increment it
Sample
SELECT last_number
FROM all_sequences
WHERE sequence_owner = '<sequence owner>'
AND sequence_name = '<sequence_name>';
For detail follow this question How to retrieve the current value of an oracle sequence without increment it?
Then do a search in the VITA_TRANSACTION where VTR_SEQ_ID= retrieved current value + 1
I am quite sure you will get one row.Now to avoid this error you can increment your sequence value by executing the below sql.
select SEQ_VITA_TRANSACTION.nextval from dual;
It will increment the value of your current sequence by 1(if your sequence INCREMENT BY 1) . Then try to again execute your insert query.
Primary Key violation usually occurs if you are inserting already exist values in the table. So you can check the records on the table VITA_TRANSACTION for the next sequence i.e SEQ_VITA_TRANSACTION.nextval, you can use following query:
Select
count(*)
From **VITA_TRANSACTION**
where
VTR_SEQ_ID = **SEQ_VITA_TRANSACTION.nextval**;
If the above query returns count as 1 then there is already a record for that sequence. Then try by increasing the sequence value or you can delete the record. It's your call.
Hope this answers your question.

Inserting a New UpdatableRecord and Receiving Error on Duplicate Primary Keys

I'm trying to insert a new record using UpdatableRecords in jOOQ 3.4.2. The pattern is extremely concise and pleasant to use, except that the INSERT reads null values as no value and ignores default values or a generated index. How can I use the UpdatableRecord to do an insert that respects default values and generated indexes?
Here's my table:
CREATE TABLE aragorn_sys.org_person (
org_person_id SERIAL NOT NULL,
first_name CHARACTER VARYING(128) NOT NULL,
last_name CHARACTER VARYING(128) NOT NULL,
created_time TIMESTAMP WITH TIME ZONE DEFAULT current_timestamp NOT NULL,
created_by_user_id INTEGER,
last_modified_time TIMESTAMP WITH TIME ZONE,
last_modified_by_user_id INTEGER,
org_id INTEGER NOT NULL,
CONSTRAINT PK_org_person PRIMARY KEY (org_person_id)
);
Note my primary key and default values. Now here's my jOOQ code:
// orgPerson represents a POJO filled with my values to be inserted and null for everything else
// Note that orgPerson.orgPersonId is null
OrgPersonRecord orgPersonRecord = create.newRecord( ORG_PERSON, orgPerson );
Integer orgPersonId = create.executeInsert( orgPersonRecord );
But when I run this, I get the error null value in column "org_person_id" violates not-null constraint.
I noticed the jOOQ docs say that calling newRecord automatically sets all the internal "changed" flags to true on the UpdatableRecord. So then I tried this:
// orgPerson represents a POJO filled with my values to be inserted and null for everything else
// Note that orgPerson.orgPersonId is null
OrgPersonRecord orgPersonRecord = create.newRecord( ORG_PERSON, orgPerson );
orgPersonRecord.changed( ORG_PERSON.ORG_PERSON_ID, false );
orgPersonRecord.changed( ORG_PERSON.CREATED_TIME, false );
orgPersonRecord.insert()
Integer orgPersonId = orgPersonRecord.getOrgPersonId();
But that gives me the error ERROR: duplicate key value violates unique constraint "pk_org_person". And when I do this repeatedly, the values seem to keep increasing by 1. This doesn't really make sense to me, but my greater question is: Is there a good way I can do an INSERT based on my object values, or better yet, simply include only the non-null columns?
I saw JOOQ ignoring database columns with default values, but that doesn't seem to resolve this. Any recommendations on the most concise way to handle this?
By the way, jOOQ has been fantastic to work with so far. Lukas, thank you for this awesome tool!
UPDATE #1:
The "not null issue" is addressed by Lukas's answer below, and that's an easy fix.
For the duplicate primary keys, I am definitely not confusing INSERT with UPDATE. When I run the above code (slight update since original post), jOOQ seems to arbitrarily pick a "starting" primary key value for OrgPersonId. For example, when I first load up my environment, jOOQ might start with "11" for OrgPersonId.
Then, when I do an INSERT, jOOQ will attempt to supply a value of "11" for OrgPersonId, I'll get the ERROR: duplicate key value and the INSERT will fail. If I then repeat the INSERT, jOOQ uses "12", then "13". It succeeds or fails based on whether that ID is available, but it's not "starting" with the right ID.
The manual (http://www.jooq.org/doc/3.4/manual/sql-execution/crud-with-updatablerecords/identity-values/) says that If you're using jOOQ's code generator, the above table will generate a org.jooq.UpdatableRecord with an IDENTITY column. This information is used by jOOQ internally, to update IDs after calling store().
UPDATE #2:
Ok, I just tried the generated query directly in Postgres and it fails there, too, with the same issue. So, clearly this is a Postgres issue and not a jOOQ issue. I'll post the final resolution on that when I find it in case anyone else runs into this.
UPDATE #3:
Issue has been resolved. We use FlywayDB (another awesome tool) to automate our database schema migration, and we had a bunch of INSERT statements in our Flyway scripts that manually INSERTED the id number. This was convenient because we wanted to create a bunch of dummy data and wanted to guarantee the right foreign key relationships.
But manually specifying the primary key increment does not advance the Postgres sequence! Hence, we had to cycle through the Postgres sequence before (correctly operating) jOOQ would get the right sequence value.
Solution is to remove all our manual inserts of the primary keys in our demo data migration scripts.
violates not-null constraint
The first part that you're describing is a flaw (#3582), which is related to a previous issue (#2700), which enforced storing null values loaded from POJOs into jOOQ Records for database columns that are NOT NULL. The fix will be in jOOQ 3.5.0, 3.4.3, 3.3.4, and 3.2.7
duplicate key value violates unique constraint "pk_org_person"
The second part probably is caused by the fact that you are really loading an existing record and then calling executeInsert() on it (observe the INSERT, which will always execute an INSERT statement). You might want to call executeUpdate(), instead

how to swap records having unique constraint in hibernate

I have 2 tables user and userinfo. userinfo table contains user_id(id of user table) column which has UNIQUE constraint.
now i have 2users(primaryUser and secondaryUser) which has records in user and userInfo tables.
The primaryInfo object contains primaryUserId and secondaryInfo object contains secondaryUserId
I want to swap the userinfo data of primaryUser to secondaryUser and viceversa. I am doing like this
primaryInfo.setUserId(secondaryUser.getId());
secondaryInfo.setUserId(primaryUser.getId());
session.update(primaryInfo);
session.update(secondaryInfo);
but when commiting the transaction it is giving error like
ERROR org.hibernate.engine.jdbc.spi.SqlExceptionHelper:147 ERROR: duplicate key value violates unique constraint "user_infos_unique_user"
Detail: Key (ui_user_id)=(52560087) already exists.
can you please tell how to do this.. Thanks
You can use the DEFERRABLE and INITIALLY DEFERRED properties on the constraint and update both records in a single transaction. DEFERRED means the constraint will not be evaluated until the transaction is commited -- at which time it should be valid again.
However: I have not figured out how to use Hibernate annotations to specify the DEFERRED properties, so you will have to use LiquiBase to maintain the database schema (not a bad idea anyway.) (Or use "raw" SQL which is not so good an idea.)
See this question for more about the annotations (alas I cannot use LiquiBase on the project I ask about there.)
For Oracle database you can create next unique constraint with special attributes 'DEFERRABLE INITIALLY DEFERRED':
ALTER TABLE table_name ADD CONSTRAINT constraint_name UNIQUE (table_field) DEFERRABLE INITIALLY DEFERRED
A possible trick to work around the unique constraint is to do 3 updates:
update row A with a value for the column that no other row can use. NULL may be used if not forbidden by a not-NULL constraint, otherwise 0 if not forbidden and assuming it's an integer, otherwise a negative value.
then update row B with its final value (the previous value from row A)
then update row A with its real final value (the previous value from row B)
As error Shows:
there is a unique constraint on userInfo table. that means user must be unique. So If you wnat to swipe the two user Id. you have to perform following steps
1. Remove the constraint
2. Swap two id's(same code as you currently have)
3. Add Constaint.

dictionary data insert or select (oracle, java)

I have an table (in ORADB) containing two columns: VARCHAR unique key and NUMBER unique key generated from an sequence.
I need my Java code to constantly (and in parallel) add records to this column whenever a new VARCHAR key it gets, returning the newly generated NUMBER key. Or returns the existing NUMBER key when it gets an existing VARCHAR (it doesn't insert it then, that would throw an exception of course due to the uniq key violation).
Such procedure would be executed from many (Java) clients working in parallel.
Hope my English is understandable :)
What is the best (maybe using PL/SQL block instead of Java code...) way to do it?
I do not think you can do better than
SELECT the_number FROM the_table where the_key = :key
if found, return it
if not found, INSERT INTO the_table SELECT :key, the_seq.NEXT_VAL RETURNING the_number INTO :number and COMMIT
this could raise a ORA-00001(duplicate primary key insert)
if the timing is unlucky. In this case, SELECT again.
Not sure if JDBC supports RETURNING, so you might need to wrap it into a stored procedure (also saves database roundtrips).
You can use an index-organized table (with the_key as primary key), makes the lookup faster.

Categories