Hibernate: #GeneratedValue(strategy = GenerationType - java

I am using DB2 for my application. I run some insertion script after creating database. That insertion script generates records in table with id's given in insertion script.
Suppose for abc table insertion script creates a record with id = 3. As id’s are set to auto generated in hibernate so while saving third record from application I got exception.
Caused by: com.ibm.websphere.ce.cm.DuplicateKeyException: One or
more values in the INSERT statement, UPDATE statement, or foreign
key update caused by a DELETE statement are not valid
because the primary key, unique constraint or unique
index identified by "1" constrains table
I am using #GeneratedValue(strategy = GenerationType.AUTO)
What strategy = GenerationType I should use to overcome this problem.

There are issues with certain Databases and Hibernate when you use GenerationType.IDENTITY. Try using a sequence and explicitlly configure everything for it:
#Id
#SequenceGenerator(name = "DEPARTMENT_ID_GENERATOR", sequenceName="department_sequence", allocationSize=100)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator = "DEPARTMENT_ID_GENERATOR")
#Column(unique = true, nullable = false)
protected Long id;

For DB2 #GeneratedValue(strategy = GenerationType.IDENTITY) should work correctly.

If ids are provided in file then you don't need #GeneratedValue at all as there is no id to generate. And make sure to clean database as #SjB suggested.
Also, without knowing much about DB2, the error message suggests that there may be other violation than just duplicate id on insert. Are there any foreign keys involved?

Nothing works except this query.
alter table TABLE_NAME alter column ID set GENERATED BY DEFAULT RESTART WITH 10000;
DB2 should choose available ID itself but not doing so.

Related

Hibernate Generation Type for SQL Server and Others

I wrote program that uses different databases like sql server, oracle etc. My problem is that I can't handle GenerationType and insert correct row into table. Using GenerationType.AUTO and hibernate.id.new_generator_mappings := false in sql server, my program is able to insert new row into table, but ID is always null, same problem is when GenerationType is IDENTITY.
I tried to add auto-incrementation only for sql server, but Liquibase yells at me that it's not supported for mssql. When I use Sequences for Oracle as well SQL Server my program is trying to get "next value" from generator but it cannot and do infinite loop. Even if I set default value for ID it won't increment this value.
Thats my code :
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "name")
#SequenceGenerator(name = "name", sequenceName = "SEQ", allocationSize = 1)
private Long id;
I would like to be able to add auto-incrementing indices into table and it should work for SQL Server databases and I don't want to use Table strategy for generation because it needs additional table in db.
Problem solved. I add condition in Liquibase xml file that checks whether db is mssql type and if it's true script drops ID column and adds it with IDENTITY(1,1) option.
The only problem is that now I have to switch aforementioned "hibernate.id.new_generator_mappings" setting.

How force hibernate to sync sequences?

I try to prepare an integration test with test data. I read insert queries from an external file and execute them as native queries. After the insertions I execute select setval('vlan_id_seq', 2000, true );. Here is the entity ID definition:
#Id
#Column(name = "id", unique = true, nullable = false)
#GeneratedValue(strategy = IDENTITY)
private Integer id;
When I try tor persist a new entry, I got a Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "vlan_pkey"
Detail: Key (id)=(1) already exists. exception. The ID of the sequence is 2000. The column definition is done by the serial macro and is id integer NOT NULL DEFAULT nextval('vlan_id_seq'::regclass).
I executed the native queries in a user transaction, so all test entries are stored in the postgresql data base, but it seems that hibernate not sync the sequence. The entityManager.flush(); also didn't force a sequence synchronisation. It seems that hibernate did not use sequences with #GeneratedValue(strategy = IDENTITY). I use a XA-Datasource and wildfly 13.
I tested now an other initialisation method. I defined a SQL data script (I generated the script with Jailer) in the persitence.xml (javax.persistence.sql-load-script-source) and end the script with select pg_catalog.setval('vlan_id_seq', (SELECT max(id) FROM vlan), true );. I set a breakpoint before the first persist command, check the sequence in the postgresql db, the sequence has the max id value 16. Now persisting works and the entry has the id 17. The scripts are executed before the entity manager is started and hibernate read the the updated sequences while starting. But this solution did not answer my question.
Is there a possibility that hibernate reread the sequences to use the nextval value?
if the strategy is Identity this means hibernate will create a sequence table and fetch the IDs from it, by using native sql you are just inserting your own values without updating that table so you have TWO solutions
Insert using hibernate itself which will be fairly easy, in your
integration test inject your DAOs and let hibernate do the insertion
for you which is recommended so you do not need to rehandle what
hibernate already handled
Update the sequence table whenever you do the insert by increment the
value which I do not recommend.

Force hibernate to leave id empty

So I am using Postgres and Hibernate 4.2.2 and with entity like this
#Entity(name = "Users")
#Check(constraints = "email ~* '^[A-Za-z0-9._%-]+#[A-Za-z0-9.-]+[.][A-Za-z]+$'")
#DynamicInsert
public class Users {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "id_user",unique = true)
#Index(name = "user_pk")
private Integer idUser;
Hibernate still inserts some id that is already in the table, instead of leaving it emtpy for the database to fill it in. Also hibernate forces ids based on its cache not even checking the database whether it has the lates id.
How can I force it so I can leave id blank and let the database insert it?
First I thought it was because I was using int and that int is by default 0 but even when using object it just forces the id there from its cache.
So my goal is to let the database fill the ids instead of hibernate or at least Hibernate before filling it in to check the database for id first.
So the error I was getting wasCaused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "users_pkey" Detail: Key (id_user)=(1) already exists.
And it wasn't caused by Hibernate and caching but by import of data at creation of database, where I inserted with given ids eg: INSERT INTO users(id_user,email,password,tag) VALUES (1,'a#b.c','***','Adpleydu');
and the sequence for generating wasn't updated so if I inserted with pure SQL via console I got the same error.
Seeding the data is the problem. However you can still seed with pure sequal and have the sequence "keep up".
1) Assure your primary key is of type SERIAL.
CREATE TABLE table_name(
id SERIAL
);
2) Add this 'setval' line to assure the sequence is updated.
select setval('table_name_id_seq',COALESCE((select max(id) + 1 from table_name), 1));
Reference:
https://www.postgresqltutorial.com/postgresql-serial/

Should hibernate use unique sequences for each table?

I have several entities using AUTO key generation strategy with Hibernate and postgres.
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
This will result in a hibernate_sequence generated, and each entity will make use of that sequence when assigning keys.
Now I have a table that has lots of cache data (like 100k entries), and some user tables. As both use strategy AUTO, they both get their keys from the same hibernate sequence. As a result, even if I only have 10 users, they will all have an id of 6-7 digits long, like 123123.
I wonder if, in general, one should introduce a custom sequence for each table? Or shouldn't I care about the id generation that much?
I have recently solved this problem for my project. I use the Enhanced sequence generator (which is the default for sequence-style generators) and set the prefer_sequence_per_entity parameter to true.
Contents of my package-info.java:
#GenericGenerator(
name = "optimized-sequence",
strategy = "enhanced-sequence",
parameters = {
#Parameter(name="prefer_sequence_per_entity", value="true"),
#Parameter(name="optimizer", value="hilo"),
#Parameter(name="increment_size", value="50")})
package org.example.model;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
On the usage side you just need
#Id #GeneratedValue(generator="optimized-sequence")
public long id;
I prefer having separate sequences because occasionally I'll drop a table and recreate it, and I want the ID's starting from one.
You can use serial datatype for your useid , or use PostgreSQL sequence.
LIKE :
digoal=# create table tt(id serial, info text);
CREATE TABLE
digoal=# insert into tt (info) values ('test'),('test');
INSERT 0 2
digoal=# select * from tt;
id | info
----+------
1 | test
2 | test
(2 rows)
OR
digoal=# create table tt1(id int, info text);
CREATE TABLE
digoal=# create sequence seq_tt1;
CREATE SEQUENCE
digoal=# alter table tt1 alter column id set default nextval('seq_tt1'::regclass);
ALTER TABLE
digoal=# insert into tt1 (info) values ('test'),('test');
INSERT 0 2
digoal=# select * from tt1;
id | info
----+------
1 | test
2 | test
(2 rows)
Try with Sequence
First create sequence in postgres
CREATE SEQUENCE YOUR_ENTITY_SEQ;
In entity, use generation strategy as SEQUENCE and for next time gen value allocation set allocationSize as necessary
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "YOUR_ENTITY_SEQ")
#SequenceGenerator(name = "YOUR_ENTITY_SEQ", sequenceName = "YOUR_ENTITY_SEQ", allocationSize = 1)
private Long id;
This is just the preferred way for Hibernate to manage primary keys. It will generate the appropiate SQL idiom depending on your underlying database (a sequence for Oracle, and identity key field for DB2, etc.)
However, you can perfectly define composite keys if you feel they are more appropiate for your business. Someone gave a great explanation on this here in Stackoverflow:
How to map a composite key with Hibernate?

Hibernate throws PK violation error

I've been using hibernate with jboss 4.2.3 and everything was working, now i migrated the code to Jboss 7.1.1 and suddenly i start getting :
Caused by: org.hibernate.exception.ConstraintViolationException: ORA-00001: unique constraint (OBLICORE.PK_ACE_WORKERS_QUEUE_STATS_ID) violated
Also the generated ID's are negative.
The entity that fails is defined as such:
#Id
#SequenceGenerator(name = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", sequenceName = "SEQ_ACE_WORKERS_QUEUE_STATS_ID", allocationSize = 500)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_ACE_WORKERS_QUEUE_STATS_ID")
#Column(name = "ID")
private long Id;
I checked the sequence in Oracle and it seems O.K (Like i said, it worked before with jboss 4.2 and nothing changed on the DB side since the migration).
I tried writing Hibernate query logs but couldn't locate that query and I've also logged the specific call that persist this class and saw that it only get called once.
Check this question: hibernate oracle sequence produces large gap
It must be Hibernate's sequence generator which is defaulting to the Hi/Lo algorithm, and the returned values overflowing. You can try using a hibernate-specific annotation to default to the older behaviour GenericGenerator(name="blah", strategy="sequence"), or set allocationSize=1.
If you are relying on your sequence incrementing by some value larger than 1, you'll have to use a different generator. Or maybe it's enough to set hibernate.id.new_generator_mappings to false, but that is in the scope of a new question.
When we changed Hibernate to use the new generator, I used the following script to fix the sequences:
DECLARE
v NUMBER;
BEGIN
FOR r IN (select sequence_name from user_sequences) LOOP
EXECUTE IMMEDIATE 'ALTER SEQUENCE '|| r.sequence_name ||' INCREMENT BY 50';
EXECUTE IMMEDIATE 'SELECT '|| r.sequence_name ||' .NEXTVAL FROM DUAL' INTO v;
END LOOP;
END;
/
If your allocationSize is 500, you should change the "INCREMENT BY 50" to "INCREMENT BY 500".
If the generated value of id is not so critical in your project try to use #GeneratedValue(strategy = GenerationType.AUTO)
this strategy will generate id avtomatically by incrementing last by one. Hope it will useful for you.

Categories