How to use existing Oracle sequence to generate id in hibernate? - oracle

I have legacy Oracle db with a sequence named PRODUCT_ID_SEQ.
Here is the mapping of Product class for which I need generate correct ids:
public class Product {
#GeneratedValue(strategy = GenerationType.SEQUENCE,
generator = "retailerRaw_seq")
#SequenceGenerator(name = "retailerRaw_seq",
sequenceName = "PRODUCT_ID_SEQ")
private Long id;
...
}
But looks like ids are generated with an interval of 50, like 1000, 1050, 1100 etc. This corresponds to the default value of allocationSize property = 50. So that means that Hibernate doesn't actually use the sequence which is already defined in the db.
How do I make Hibernate use the sequence?

The answer to the original question:
#SequenceGenerator(name="EL_SEQ", sequenceName="EL_SEQ",allocationSize=1)
It is allocationSize that sets the value to increment by.

I'm not used to use annotations, this is what I have in my *.hbm.xml:
<id name="id" type="java.lang.Integer">
<column name="ID_PRODUCT" />
<generator class="sequence-identity" >
<param name="sequence">PRODUCT_ID_SEQ</param>
</generator>
</id>
You can easily map this to annotations. The generator sequence-identity uses auto increment with sequences.

Here is a working example with annotations, this way, the existing DB sequence will be used (you can also use the "sequence" strategy, but with less performance on insertion) :
#Entity
#Table(name = "USER")
public class User {
// (...)
#GenericGenerator(name = "generator", strategy = "sequence-identity", parameters = #Parameter(name = "sequence", value = "USER_SEQ"))
#Id
#GeneratedValue(generator = "generator")
#Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
public Long getId() {
return this.id;
}

I had the same issue while upgrading from 3.5.5 to 5.0.6.Final.
I solved it by re-configuring mapping in the HBM file from:
<generator class="sequence">
<param name="sequence">PRODUCT_ID_SEQ</param>
</generator>
to:
<generator class="org.hibernate.id.enhanced.SequenceStyleGenerator">
<param name="prefer_sequence_per_entity">true</param>
<param name="optimizer">none</param>
<param name="increment_size">1</param>
<param name="sequence_name">PRODUCT_ID_SEQ</param>
</generator>

Create your sequence name in Oracle, for example, contacts_seq.
In Your POJO Class . Define the following annotation for your sequence.
#Id
#GeneratedValue(strategy=GenerationType.AUTO, generator="my_seq_gen")
#SequenceGenerator(name="my_seq_gen", sequenceName="contacts_seq")

If you use javax.persistence.SequenceGenerator, hibernate use hilo and will possibly create large gaps in the sequence. There is a post addressing this problem: https://forum.hibernate.org/viewtopic.php?t=973682
there are two ways to fix this problem
In the SequenceGenerator annotation, add allocationSize = 1, initialValue= 1
instead of using javax.persistence.SequenceGenerator, use org.hibernate.annotations, like this:
#javax.persistence.SequenceGenerator(name = "Question_id_sequence", sequenceName = "S_QUESTION")
#org.hibernate.annotations.GenericGenerator(name="Question_id_sequence", strategy = "sequence", parameters = { #Parameter(name="sequence", value="S_QUESTION") } )
I have tested both ways, which works just fine.

allocationSize and incrementBy are completely different things.
Hibernate is of course using your sequence created in DB but depending on allocationSize you might find gap in generated value.
For example-
Let assume current sequence value is 5, increment by 1 in db, and allocationSize default 50.
Now you want to save a collection of 3 element through hibernate, then
Hibernate will assign generated id 250, 251, 252
This is for optimization purpose. Hibernate doesn't have to go back to db and fetch next incremented value.
If you don't want this just setting allocationSize = 1 as already answered will do the purpose

I use following on PostgreSQL and works just fine.
#Id
#GeneratedValue(generator = "my_gen")
#SequenceGenerator(name = "my_gen", sequenceName = "my_seq_in_db")
private int userId;

By default Hibernate uses sequence HiLo generator which ,unless you have special needs, it is good (performance wise). You can read more of that in my blog here
Eyal

First : you should create in your database the sequence like:
CREATE SEQUENCE "PRODUCT_ID_SEQ" MINVALUE 0 MAXVALUE 1000000000 INCREMENT BY 1 START WITH 1 CACHE 500 NOORDER NOCYCLE ;
and in your file Product.hbm.xml configuration make :
<class name="ProductPersistant" table="Product">
<id name="id" type="java.lang.Long" column="productID" >
<generator class="sequence">
<param name="sequence">PRODUCT_ID_SEQ</param>
</generator>
</id>

Related

Spring JPA sequence generator initial value is not working

I have one entity that already have records in database (20 records).
My primary key column is already defined as SERIAL in a postgres database.
My entity:
#Entity
#Table(name = "foo", schema = "public")
#Data
#Builder
#AllArgsConstructor
#NoArgsConstructor
public class FooEntity {
#Id
#SequenceGenerator(name="foo_seq", sequenceName = "foo_seq", initialValue = 20, allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "foo_seq")
#Column(name = "id")
private Integer id;
}
I run a query that returns all existing sequences in database and i find the "foo_seq".
In my service, when i try to call my repository to save the data, its returned this error:
FooEntity fooEntity = FooEntity.builder()
.id(null)
.build();
FooEntity savedEntity = fooRepository.save(fooEntity);
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key
value violates unique constraint "foo_pkey" Detail: Key (id)=(5)
already exists.
Seems that spring is not using the value defined in initialValue (should try to save with id 20, but instead, is using your own increment starting with 1). When i call this method again, the increment is added, but not starting with 20.
I'm missing something? Should i have alter this sequence in postgres to start with id 20?

Consuming sequence inside a embeddedId in springboot

I have the next issue -> I have a table on my db with a composite id...
Supose (Id1,Id2,Id3,Id4), the Id4 is generated by a sequence on the db...
My question is, in spring boot, I generate the entity 'Table1' and the corresponding 'Table1Id',
but when i want to add the corresponding GeneratedValue from the sequence, is not generating anything.
I was looking for in the internet and i found that the GeneratedValue is not working without the #Id anotation, but maybe there are some way to fix this issue.
Thank's and sorry for my english.
SOLVED:
When a composite id is required in your project, it is impossible with an embeddedId. It is necesary to use #IdClass on my compossiteId instead #EmbeddedId, because the second does not work with #GeneratedValues for example my solution was:
#Data
#Entity(name = "table_name")
#IdClass(CompositeIdTest.class)
public class TestClass implements Serializable {
#Id
#Column(name = "column", nullable = false)
private String column;
#Id
#SequenceGenerator(name = "sequence", sequenceName = "sequence", allocationSize = 1)
#GeneratedValue(generator = "sequence")
private int idGenerated;
Anyway, thank's

Spring Boot Hibernate not picking up use-new-id-generator-mappings property

I'm upgrading my project to Spring Boot 2.1.18 that uses Hibernate 5.3.18.
Previously, my entity looked like thus and would use the SequenceHiLoGenerator:
#Entity
#Table(name = "group_link")
#SequenceGenerator(name = "group_link_seq", sequenceName = "group_link_seq")
public class GroupLinkEntity extends BaseObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_link_seq")
#Column(name = "group_link_id", unique = true, nullable = false)
private Long id
}
Now, by default in Hibernate 5, it uses the SequenceStyleGenerator which causes constraint violations because my increment size is 1 and the default allocationSize is 50.
The suggested thing to do to maintain compatibility is to set this property:
spring.jpa.properties.hibernate.use-new-id-generator-mappings: false
I do so but it does not seem to take, because the SequenceStyleGenerator is still used. From my understanding, this should cause it to use the SequenceHiLoGenerator. Is this incorrect?
However, if I modify the entity to look like the below it works as expected, replicating the previous functionality I had.
#Entity
#Table(name = "group_link")
#GenericGenerator(
name = "group_link_seq",
strategy = "org.hibernate.id.SequenceHiLoGenerator",
parameters = {
#Parameter(name = "sequence_name", value = "group_link_seq"),
}
)
public class GroupLinkEntity extends BaseObject {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "group_link_seq")
#Column(name = "group_link_id", unique = true, nullable = false)
private Long id;
}
So, it would seem the property is not being taken somehow and I'm looking to figure out why that is. I see it show up in my JpaProperties bean. If I change other properties, like my dialect, I can see that they are taking effect.
Could anyone point me to the code that actually reads that property and makes a decision on what generator to use or point out some obvious error I'm making here?
As it's stated in the documentation:
You need to ensure that names defined under spring.jpa.properties.* exactly match those expected by your JPA provider. Spring Boot will not attempt any kind of relaxed binding for these entries.
For example, if you want to configure Hibernate’s batch size you must use spring.jpa.properties.hibernate.jdbc.batch_size. If you use other forms, such as batchSize or batch-size, Hibernate will not apply the setting.
So, for your case you should use:
spring.jpa.properties.hibernate.id.new_generator_mappings: false
See also this part of hibernate documentation.

Hibernate generated id cause error message : "key already exists"

I'm using spring app with hibernate and postgres to store data. The configuration for the product entity is as follow :
/**
* A Product.
*/
#Entity
#Table(name = "product")
#Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
#Document(indexName = "product")
public class Product implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator")
private Long id;
...
}
When I want to create a product using the web app I get the following error duplicate key value violates unique constraint. Detail : the key (id)=(35018) already exists.
From my understanding hibernate use a sequence in db to generate the next id value. So I did SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'; in the psql shell to get all sequence in my db. The output is :
hibernate_sequence
jhi_user_id_seq
key_value_id_seq
jhi_persistent_audit_event_event_id_seq
unit_id_seq
generic_user_id_seq
currency_id_seq
customer_type_id_seq
customer_exploitation_type_id_seq
legal_entity_id_seq
deposit_id_seq
machine_id_seq
tank_id_seq
address_id_seq
product_id_seq
rewarded_file_id_seq
bar_code_type_id_seq
quality_label_id_seq
shop_pdv_id_seq
brand_id_seq
category_id_seq
material_id_seq
ws_call_id_seq
postal_code_id_seq
commune_id_seq
country_id_seq
event_id_seq
event_type_id_seq
key_blob_id_seq
card_id_seq
So I thought nice I have a product_id_seq and I only have to update the value in it for things to work.
But when I request the value with SELECT * FROM product_id_seq; I get :
last_value | log_cnt | is_called
------------+---------+-----------
100616 | 0 | t
So here I think that the id generated for the product id is not coming from this product_id_sequence since it tries to insert a product with id = 35018 and the product_id_seq last value is 100616.
So I wonder where does this id 35018 comes from? Which sequence is used to generate it? My guess is I have to update this mysterious sequence to get things to work. For info the hibernate sequence has a value of 36400.
Any idea that could get me going? Thanks in advance.
You do not map your sequence with postgre sequence so Hibernate creates the sequence hibernate_sequence (the one you got 35018 from) for itself.
To use your existing sequence:
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
#SequenceGenerator(name = "sequenceGenerator", sequenceName = "product_id_seq")
private Long id;

org.hibernate.exception.SQLGrammarException: could not get next sequence value and sequence does not exit

I'm using oracle sql developer and create a table named avatar. It has two columns , avatarid and image .
I also create a trigger and sequence for id auto generation .
But when I use this table with hibernate and spring tool suite , I got an exception ,that is
org.hibernate.exception.SQLGrammarException: could not get next
sequence value.
It says
sequence does not exit
I try all the answers but can't fix the error. In Sprig Tool Suite , I connect to database using datasource and search my sequence under my schema , and my sequence doesn't exit.
I created the sequence along with trigger using Oracle SQL Developer.
Please give me some more advice.
Here is my Avatar.hbm.xml
<hibernate-mapping>
<class name="com.bluestone.fileupload.model.Avatar" table="avatar">
<id name="avatarid" type="int">
<column name="AVATARID" />
<generator class="sequence">
<param name="my_seq">SU_AVATAR_SEQUENCE</param>
</generator>
</id>
<property name="image" type="binary">
<column name="IMAGE" not-null="true" />
</property>
</class>
</hibernate-mapping>
And , this is my Avatar.class
#Entity
#Table(name = "avatar")
public class Avatar{
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
private int avatarid;
private byte[] image;
public Avatar() {
}
public Avatar(int avatarid, byte[] image) {
super();
this.avatarid = avatarid;
this.image = image;
}
public void setAvatarid(int avatarid) {
this.avatarid = avatarid;
}
.
.
1) Make sure is the sequence is available in the database
2) May be you need to use schema say eg : HR.SU_AVATAR_SEQUENCE
where HR is the schema name
3)Else create sysnonym for schema
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "my_seq_gen_9")
#SequenceGenerator(name = "my_seq_gen_9", sequenceName = "SEQ_NAME_YOUR")
#Column(name = "COLUMN")
private long columnName;
Sequence can used as in hibernate.
I faced the same issue.
My Resolution was:
#SequenceGenerator(name = "ID_GENERATOR", sequenceName = "ACTIVITY_SEQ", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ID_GENERATOR")

Resources