Spring Data/JPA PostgreSQL SequenceGenerator and duplicate primary keys on concurrent access to the same database - spring

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 ?

Related

#OneToMany Column not showing?

I am new to Hibernate and curious, why my column is not showing.
So I have following code (in kotlin):
#Entity
class Project (
var guid: String = "",
#OneToMany(mappedBy = "project", cascade = [CascadeType.ALL])
var tickets: List<Ticket?>,
var current: Date
){
#Id #GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = 0
}
and the other entity:
#Entity
class Ticket (
var guid: String = "",
#ManyToOne
#JoinColumn(name="project_id")
var project: Project?,
var current: Date
){
#Id #GeneratedValue(strategy = GenerationType.AUTO)
var id: Long = 0
}
In my database, the table "ticket" has a column project_id, with null values.
BUT my other table "Project" has no column for the list of Tickets. What went wrong? Every help will be appreciated, please dont forget to explain.
Thank you!
The reason your Project table has no column for the list of tickets, is that relational databases are designed to have one value per cell. You cannot store a list or array of something in a table. The solution for this is to create another table (let's call it the child table) that references this table (the parent table). Each row in the child table will be one entry of that list, and stores a reference to the parent list. So in a way, the relationship has been inverted: instead of storing the tickets in the Project table, a reference to the Project table is stored in the Ticket table.
In your case, you're indicating that the project_id should be used as a reference, since you're mentioning it in the #JoinColumn. However, there is no such property in the Project table, so you should add it. Hibernate is expecting that this is the column to link the two tables.
Also: don't forget to create a foreign key constraint to the parent table on the column that you're using in the Ticket table either.

How to migrate [DDL] from generation type identity to generation type sequence for postgres db

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.

Hibernate insert with id generation on insert trigger with sequence

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

Hibernate Jpa - constraint violation exception on Primary Key(Sequence)

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

Primary Key Violation on JPA save method

I'm facing an annoying problem, which I think it's really stupid.
I'm using an H2 database with JPA and Hibernate.
This is my save method on ScheduleService.class:
#Transactional(propagation = Propagation.REQUIRES_NEW)
public int save(Schedule sched) {
try {
scheduleRepository.save(sched);
} catch(Exception e) {...}
}
The sentence "scheduleRepository.save(sched)" is throwing the following exception:
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["UK_4R97H1MMVBOG9TD05OH9FKL16_INDEX_5 ON PUBLIC.SCHEDULE(CHANNEL, PROGRAMME, START, END) VALUES ( /* key:65574 */ null, TIMESTAMP '2015-09-15 17:00:00.0', null, TIMESTAMP '2015-09-15 16:40:00.0', 3, 13)"; SQL statement:
insert into schedule (channel, end, programme, published, start, idSched) values (?, ?, ?, ?, ?, ?) [23505-175]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
And printing to the output console the following message:
Unique index or primary key violation: "UK_4R97H1MMVBOG9TD05OH9FKL16_INDEX_5 ON PUBLIC.SCHEDULE(CHANNEL, PROGRAMME, START, END) VALUES ( /* key:65574 */ null, TIMESTAMP '2015-09-15 17:00:00.0', null, TIMESTAMP '2015-09-15 16:40:00.0', 3, 13)"; SQL statement:
insert into schedule (channel, end, programme, published, start, idSched) values (?, ?, ?, ?, ?, ?) [23505-175]
I've removed my #UniqueConstraint annotation in Schedule entity to make sure it is not a unique constraint problem.
The error persists, so it must be caused by a primary key violation.
This is how I define the primary key of Schedule entity:
#Id
#Column(name = "idSched", nullable = false)
#GeneratedValue(strategy = GenerationType.TABLE)
protected Long idSched;
It should be assigning a new unique idSched field for every row is inserted in the table, right?
What could be the problem which is causing the exception?
Thank you.
UPDATE:
Based on Bohuslav suggestion, I've changed the primary key declaration as this:
#Id
#TableGenerator(
name="scheduleGen",
table="schedule_sequence_table",
pkColumnValue="idSched",
allocationSize=1)
#GeneratedValue(strategy = GenerationType.TABLE, generator="scheduleGen")
#Column(name = "idSched", nullable = false)
protected Long idSched;
For the moment it seems to work fine.
UPDATE 2: Based on Sreenath comment:
I'm generating the tables only once (they're persisted as files on disk). Actually there was a unique key constraint on schedule table. However, since it was throwing that exception, I removed the constraint and removed database files to make sure the problem was not in the unique constraint but in the primary key instead. Even without the unique constraint and a brand new database, the exception still remained, so then I came with the #TableGenerator solution. Apparently that solved the problem.
But then I put my unique constraint again, removed database files so tables can be regenerated, and started and stopped the application a few times.
Now the problem has come again.
This is my unique constraint:
#Table(uniqueConstraints={#UniqueConstraint(columnNames = {"channel", "programme", "start", "end"})})
#Entity(name = "schedule")
public class Schedule { ...}
Of course, before I save an entity, I check if there is any other entity with the same unique members as the one I'm adding:
Schedule schedIn = schedRep.findOneByChannelAndProgrammeAndStartAndEnd(
sched.getChannel(), sched.getProgramme(), sched.getStart(), sched.getEnd());
// Check if there exists any schedule with the same {channel, programme, start, end} values
if (schedIn == null) {
// If not, then save it
schedRep.save(sched);
}
The method schedRep.findOneByChannelAndProgrammeAndStartAndEnd() is automatically generated by JPA. So that's weird, that query cannot find any schedule with the same {channel, programme, start, end} values, but when I try to save them, the unique constraint is violated.
Where could be the problem then?

Resources