I use Hibernate JPA in my application. I have a Table that has a Primary key(Sequence). Service inserts records to that table.
Version: Oracle 12c
Dialect: org.hibernate.dialect.Oracle10gDialect
Issue :
We face problem(Unique constraint violation on SEQUENCE Key) during the load testing.
Questions :
This issue is not occurring all the time. But only during load test. Can someone please check and help to use thread safe generator?
Is it DB side sequence definition issue or at Java side?
DB Sequence :
CREATE SEQUENCE MY_SEQ
START WITH 1
INCREMENT BY 1
NOMINVALUE
NOMAXVALUE
CACHE 30
NOORDER;
CREATE TABLE MY_TABLE (
MY_PRIMARY_KEY INT default MY_SEQ.nextval NOT NULL,
VALUE_COL VARCHAR2(10) NULL
);
Entity :
public class MyTableEntity implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "MY_PRIMARY_KEY")
#GenericGenerator(
name = "mySequenceGenerator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "SEQUENCE MY_SEQ"),
#Parameter(name = "increment_size", value = "1")
}
)
#GeneratedValue(generator = "mySequenceGenerator")
private long myPrimaryKey;
#Column(name = "VALUE")
private String value;
}
Oracle 10 Dialect
For Oracle10gDialect use this configuration
#Id
#Column(name = "MY_PRIMARY_KEY")
#GeneratedValue(strategy=GenerationType.AUTO)
Long myPrimaryKey;
Hibernate creates a table and a sequence:
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) not null,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
create sequence hibernate_sequence
While storing it first gets the new sequence ID and than passes it in the INSERT statement
select hibernate_sequence.nextval from dual
insert into MY_TABLE (VALUE, MY_PRIMARY_KEY) values (?, ?)
Oracle 12 Dialect
If you use Oracle 12 that natively supports IDENTITY column it is prefered to upgrade to Oracle12cDialect (note that this requires Hibernate 5.3)
Set the strategy to GenerationType.IDENTITY
#Id
#Column(name = "MY_PRIMARY_KEY", updatable = false, nullable = false)
#GeneratedValue(strategy=GenerationType.IDENTITY)
Long myPrimaryKey;
The following table is created - the important part is generated as identity which provides the unique velues.
Note that no explicite sequence is required to be created, it is managed internally .
create table MY_TABLE (
MY_PRIMARY_KEY number(19,0) generated as identity,
VALUE varchar2(255 char),
primary key (MY_PRIMARY_KEY))
While storing no ID is passed in the INSERT, it is assigned by Oracle and returned to the session
insert into MY_TABLE (VALUE) values (?) RETURNING MY_PRIMARY_KEY INTO ?
Note that in contrary to the Oracle 10 you save one round trip to the database.
Change long to Long
Because if you use long, it (by default) is 0. No generator is allowed to change existing values!
https://docs.oracle.com/javaee/7/api/javax/persistence/GeneratedValue.html
Related
Let's say I have an entity Entry with a Clob column like:
#Entity
public class Entry {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_GENERATOR")
private Long id;
#Lob
#Column(name = "value")
private String data;
...
And in database we have an existing Entry row where VALUE is a clob initialized with Oracle's EMPTY_CLOB() function.
I need to clone the existing to a new entity, but after querying and setting an empty String for simulate the EMPTY_CLOB() from Java side, Oracle's show up a (null) value instead of an empty Clob.
Whe use hibernate as JPA implementation.
Expected after saving entity 2 with JPA:
SELECT * FROM ENTRY
ID VALUE
1 <------------ Returning an empty CLOB here
2 (null)
Actual behaviour
SELECT * FROM ENTRY
ID VALUE
1 <------------ Returning an empty CLOB here
2 <------------ Returning an empty CLOB here
It's because an empty string in Java is not NULL, and then the ORM generates an INSERT with "" for that column and the DEFAULT defined in the DDL is not applied, and eventually the '' is converted to NULL before caste-ed to CLOB.
With Hibernate, you may try to put #org.hibernate.annotations.Entity(dynamicInsert = true) or #DynamicInsert on the entity and don't set the column to empty string: if NULL, dynamic insert should skip it when generating the INSERT statement, so the DEFAULT defined in the DLL should be applied.
(Be aware of https://hibernate.atlassian.net/browse/HHH-7074)
(And you may also need dyanmicUpdate = true)
I have a spring boot app which generates a schema with flyway in an hsqldb.
The problem is that all table and column names are converted to upper case (TBL_ROLE, ID, NAME).
The aim is to have the names in the db exactly like in my script defined.
Flyway script:
create table tbl_role (
id integer not null,
name varchar(255),
default_layout varchar(255),
primary
key (id))
Entity:
#Entity
#Table(name = "tbl_role")
public class Role {
#Id
private Integer id;
private String name;
...
Hibernate Output:
org.hibernate.SQL : create table tbl_role (id integer not null, default_layout varchar(255), name varchar(255), possible_layouts varchar(255), primary key (id))
I tried to write the table and column names in double qoutes and single quotes but this didn't had any effect.
Any ideas how to achieve this?
Problem was that the flyway scripts weren't executed. So the changes with the double quotes weren't applied.
Nevertheless it's not a perfect solution..
As of now I am generating id using
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
the sequence generated in DB is like:
CREATE SEQUENCE public.table_name_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE public.table_name_id_seq OWNED BY public.table_name.id;
ALTER TABLE ONLY public.table_name ALTER COLUMN id SET DEFAULT``nextval('public.table_name_id_seq'::regclass);
or at some places in definition itself:
CREATE TABLE "public.table_name" (
"id" int8 NOT NULL DEFAULT nextval('table_name_id_seq'::regclass),
"some_column" varchar(255),
PRIMARY KEY ("id")
);
I am using hibernate as JPA provider, I needed to enable insert and update batching due to which I want to change the generation type to SEQUENCE from IDENTITY.
After changing to generation type sequence [in hibernate] and using older sequence [of postgres] i see weird id being generated. This is not the expected behavior.
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "table_name_id_seq ")
private Long id;
The ids which are highlighted in yellow is created after using generation type sequence. I was expecting id after 23 to be 24 and not -22.
How can I fix this? How can I smoothly shift from IDENTITY to SEQUENCE? What is the proper DDL?
Answering to this part of your question:
How can I smoothly shift from IDENTITY to SEQUENCE? What is the proper DDL?
You should do the following things:
Drop the default value from the table_name.id column:
alter table only table_name
alter column id drop default;
Correct your mapping for the id field:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_generator")
#SequenceGenerator(name = "my_generator", sequenceName = "table_name_id_seq", allocationSize = 1)
private Long id;
Please note that an allocationSize should be equal to the INCREMENT BY of your sequence definition.
Here my problem, I have a sequence in my oracle database and a trigger on insert to fetch the next value from the sequence and put it as id. With a tool like sql developer, it works perfectly.
My id is defined at this
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "MY_SEQUENCE")
#SequenceGenerator(sequenceName = "MY_SEQUENCE", allocationSize = 1, name = "MY_SEQUENCE")
private BigInteger jobId;
The problem is hibernate firstly read the next value of the sequence, set it as the id and then persist it. Then my database update my id with the next value of the sequence but that new id isn't "updated" in my code after my .save(entity).
I read that I should use the GenerationType.IDENTITY but I would like to do batch inserts and I also read that with IDENTITY the batch inserts is not possible.
If possible, I would like to keep my trigger so like that hibernate doesn't have to call the database each time I insert and be able to do batch inserts.
Edit: I'll probably need to insert near a million of rows
In my Spring Data/JPA project I use ProstgreSQL database.
I use a following mapping for my JPA entity PK on cards table:
#Id
#SequenceGenerator(name = "cards_id_seq", sequenceName = "cards_id_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.AUTO, generator = "cards_id_seq")
private Long id;
Everything works fine until some other applications or persons do not insert manually new records into this table. Once it happens my application fails with the following error:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "cards_pkey"
Detail: Key (id)=(42) already exists.
when tries to insert records because of out of sync between PostgreSQL sequence object and actual PK IDs in the database.
What am I doing wrong and how to solve this situation in order to assign the correct IDs to new records inserted via my application ?