Hibernate ignores initialValue for sequence generator - java

I need to have a unique ID across all tables in my database that is starting with an offset, let's say 1000. The IDs below that offset are reserved for special records that are referenced by constants in the Java code. In other words, the first record that is persisted in any table I would expect to have ID 1000, the next one 1001 etc.
I followed the instructions in the Hibernate documentation and various posts and these are the annotations I'm using:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "my_sequence", initialValue = 1000, allocationSize = 10)
private long id;
Unfortunately Hibernate completely ignores the initialValue setting. It generates the following SQL code:
create table hibernate_sequence (next_val bigint)
insert into hibernate_sequence values ( 1 )
I also had a try with the TABLE strategy which also does not work. The ID starts with 1.
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#TableGenerator(name = "my_sequence", initialValue = 1000, allocationSize = 10)
private long id;
By the way, this does work with EclipseLink JPA provider. My Hibernate version is 5.2.7 and I am going through the JPA API. I am using MySQL.
Has anybody experienced this problem?
Thanks in advance.

The generator name must be included in the #GeneratedValue annotation, such as:
#GeneratedValue(strategy = GenerationType.TABLE, generator = "my_sequence")
#TableGenerator(
name = "my_sequence",
table = "id_gen",
pkColumnName = "gen_name",
valueColumnName = "gen_val",
initialValue = 1000,
allocationSize = 10)

I think you have missed some attributes on both of the strategies:
Sequence
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
#SequenceGenerator(name = "my_sequence", sequenceName= "dbsequence"
initialValue = 1000, allocationSize = 10)
private long id;
Table
Id generation table should have two columns. The first column is a String type used to identify the particular generator sequence. It is the primary key for all the generators in the table. The Second column is an integer type that stores the actual id sequence that is being generated. The value stored in this column is the last identifier that was allocated in the sequence.
#Id
#GeneratedValue(strategy = GenerationType.TABLE)
#TableGenerator(name = "my_sequence"
, table = "id_gen"
, pkColumnName = "gen_name"
, valueColumnName = "gen_val"
, initialValue = 1000, allocationSize = 10)
private long id;
The table strategy is obviously more portable so consider that when you will make a choice.

you should name your #sequencegenerator then add the same name to #generatedvalue
you need also to create-drop your table (in application.properties) or your application is going to run the table with old sequence parameters
`#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE , name="my_sequence")
#SequenceGenerator(name = "my_sequence",sequenceName="my_sequence", initialValue = 1000,allocationSize = 10)
private long id;`

Related

Create #GeneratedValue sequence in hibernate and liquibase

In liquibase I added a script for creating a database and also use in Java / Spring Entity with GeneratedValue and my SequenceGenerator. In the case of the script in liquibase, do I have to add the generation of the identifier with the sequence to it, or is it enough that I have it from the level of my entity?
#SequenceGenerator(name = "TEST_DATA_SEQ", sequenceName = "TEST_DATA", initialValue = 1, allocationSize = 1)
public class TestPPData {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "TEST_DATA_SEQ")
#Column(name = "ID", nullable = false)
private Long id;
CREATE TABLE TEST_DATA
(
ID NUMBER(10) PRIMARY KEY,
....
);
COMMENT ON COLUMN TEST_DATA.ID IS 'Primary key';
And my LB
- changeSet:
id: test
author: test
changes:
- sqlFile:
dbms: oracle
encoding: utf8
path: scripts/test.sql
relativeToChangelogFile: true
splitStatements: true
stripComments: false
or I must need add ?
createSequence:
incrementBy: 1
sequenceName: TEST_DATA_SEQ
startValue: 1

JPA allocation Size causing Auto Generated Sequence to Reused after AllocationSize

I have recursive parent and child relation for Graph. When I have a large Graph with 50 or more nodes on a Single hibernate session, I get an error message "A different object with the same identifier value was already associated with the session". This is due to the fact the default allocation Size in Spring JPA is 50. I have overcome this error by setting allocationSize to 100 and increment by 100. But that does not solve the root of the problem. I can have any arbitrary # nodes in ONE session. I use saveAndFlush(NodeEntity) which throws this error message.
My question is How do I force Hibernate to fetch the primary key from DB after allocation size is limit is reached and be able to generate a new set primary key in a single session?
Hibernate version: hibernate-core-5.4.30.Final.jar
Error:
A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#53]; nested exception is javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#53]
//GRAPH DATABASE.
CREATE TABLE IF NOT EXISTS node
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
parent_id int,
CONSTRAINT node_pkey PRIMARY KEY (id),
CONSTRAINT node_parent_id_fk FOREIGN KEY (parent_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
);
CREATE TABLE IF NOT EXISTS graph
(
id SERIAL NOT NULL,
name character varying(255) NOT NULL,
node_id int,
CONSTRAINT graph_pkey PRIMARY KEY (id),
CONSTRAINT graph_node_fk FOREIGN KEY (node_id)
REFERENCES node (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT graph_node_id_uk UNIQUE (node_id)
);
public class NodeEntity {
#Id
#SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
#Column(name = "id")
int id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER,orphanRemoval=true)
#JoinColumn(name = "parent_id")
private List<NodeEntity> children = new LinkedList<NodeEntity>();
}
public class GraphEntity{
#Id
#SequenceGenerator(name = "graph_id_seq", sequenceName = "graph_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "graph_id_seq")
#Column(name = "id")
int id;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "NODE_ID", unique = true, nullable = true, insertable = true, updatable = true)
private NodeEntity rootNode;
}
// Way to reproduce this.
void generate150deepChild(int count,NodeEntity node){
if(count == 100){
return
}else{
NodeEntity newNode = new NodeEntity("Child " +count)
node.getChildren().add(newNode);
cout++;
generate150deepChild(count,newNode);
}
}
NodeEntity rootNode = new NodeEntity("ROOT");
// PLEASE NOT if # NodeEntity < 50 everything works fine.
generate150deepChild(0,rootNode);
// PLEASE NOT all ids are zero so they are new node.
GraphEntity graph = new GraphEntity("TEST");
graph.setRootNode(rootNode);
graphRepository.saveAndFlush(graph);
// THIS WILL GENERATE Duplicate Primary key for NodeEntity.
Actual stack trace:
Caused by: javax.persistence.EntityExistsException: A different object with the same identifier value was already associated with the session : [graph.entity.NodeEntity#131605]
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181)
at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:823)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:532)
at org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:463)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:426)
at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:220)
at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:153)
at org.hibernate.event.internal.AbstractSaveEventListener.cascadeAfterSave(AbstractSaveEventListener.java:459)
at org.hibernate.event.internal.DefaultMergeEventListener.entityIsTransient(DefaultMergeEventListener.java:247)
at org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:175)
at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:104)
at org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:813)
at org.hibernate.internal.SessionImpl.merge(SessionImpl.java:786)
at org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261)
at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:499)
at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:423)
Change to use GenerationType.SEQUENCE strategy and it should handle for you automatically :
#Id
#SequenceGenerator(name = "node_id_seq", sequenceName = "node_id_seq",allocationSize = 50)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "node_id_seq")
#Column(name = "id")
int id;
And you just need to make sure the followings:
Do not set the ID for each node manually.
The related sequence in DB (i.e. the sequence specified in sequenceName in #SequenceGenerator) is really configured and aligned with what you configure in #SequenceGenerator. In case of PostgreSQL , you can do :
alter sequence node_id_seq increment by 50;
Tip:
According to this, you can change to use the pooled or pooled-lo algorithm to reduce the DB roundtrip to get the ID by configuring the following settings:
<property name="hibernate.id.optimizer.pooled.preferred" value="pooled-lo" />

Spring Hibernate #GeneratedValue & #SequenceGenerator didn't get correct sequence as querying by the native query

Below is the entity configuration:
#Entity
#SequenceGenerator(
name = "sessionInfoIdSeq",
sequenceName = "SESSIONINFO_ID_SEQ"
)
public class SessionInfo implements Transformable {
#Id
#GeneratedValue(
strategy = GenerationType.AUTO,
generator = "sessionInfoIdSeq"
)
private Long id;
This means when inserting data into the database, the id will be fetched from the SESSIONINFO_ID_SEQ:
select nextval ('SESSIONINFO_ID_SEQ')
But the problem is, the next sequence number get by Spring boot app + Hibernate is not being the same as when we run the native query in DataGrip or DBeaver, although I've seen the app used the same query that used in Datagrip.
Spring boot + hibernate at runtime: 12749
Running native query in DataGrip: 12797
I'm not sure why this is appearing. But the question is how can I sync the sequence number, to when the app takes a new one, we can see the same on Datagrip or DBeaver.
Let me know if the question is existing or not correct. Thank you in advance for all your support.
Use can use attribute level annotations as follows:
#Id
#GeneratedValue(generator = "sequence-generator")
#GenericGenerator(
name = "sequence-generator",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "SESSIONINFO_ID_SEQ"),
#Parameter(name = "initial_value", value = "4"),
#Parameter(name = "increment_size", value = "1")
}
)
private long id;

Auto Increment in hibernate using oracle

I am new to hibernate and I want to insert primary number in my table for unique identification. I am using Oracle as my database so do I need to create sequence in oracle to get auto increment generation number ?
I am using below code but it is not working. I have not created any sequence yet.
#Id
#Column(name = "id" )
#GeneratedValue ( strategy = GenerationType.TABLE)
I have used AUTO, SEQUENCE and IDENTITY but nothing works for me.
this is one way of using Oracle sequence in a JPA mapped entity:
#Id
#Column(name = "ID")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQUENCE_NAME")
#SequenceGenerator(name = "SEQUENCE_NAME", sequenceName = "SEQUENCE_NAME", allocationSize = 1, initialValue = 1)
In this way your persist() method will ask for the next value of the sequence in order to use it as ID for your entry.
You can ue this #GeneratedValue(strategy=GenerationType.AUTO)
#Id
#Column(name = "id" )
#GeneratedValue(strategy=GenerationType.AUTO)
In GenerationType.TABLE option, ID value will be filled with the column of other table.
If you are using strategy=GenerationType.TABLE you will require to mention the Table from where your ID will be filled.
For example,
#GeneratedValue(strategy=GenerationType.TABLE, generator="course")
#TableGenerator(
name="course",
table="GENERATOR_TABLE",
pkColumnName = "key",
valueColumnName = "next",
pkColumnValue="course",
allocationSize=30
)
And for other option, you can use GenerationType.AUTO option, and let hibernate decide which option to choose according to databse.
#Id
#Column(name = "id" )
#GeneratedValue (strategy = GenerationType.AUTO)
And make sure that you properly configured hibernate.cfg.xml file.

sequence value for id in ddl2hbm

i will use sequence for primary key. I use hibernate tools in eclipse to generate ORMs. i couldn't identify an option to mention sequence for ID in java bean.
like
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "G1")
#SequenceGenerator(name = "G1", sequenceName = "LOG_SEQ")
#Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
public int getId() {
return this.id;
}
Is there a way to mention it and generate bean class with it?
In the definition below LOG_SEQ is the name of the sequence in the database that that ID should use to generate the values
#SequenceGenerator(name = "G1", sequenceName = "LOG_SEQ")
Have
java.lang.String sequenceName
(Optional) The name of the database sequence object from which to obtain primary key values.
At the end you should substitue sequenceName for the one that is in your database schema consider using
schema property of the annotation, take a look here

Categories

Resources