replacing #SequenceGenerator with #GenericGenerator - java

(Hibernate 5, PostgreSQL 9.6, Java 8, Spring ORM 4.3.6)
The following entity class Car worked fine:
import javax.persistence.*;
#Entity
#SequenceGenerator(name = "carSequence", sequenceName = "car_id_seq", )
#Table(name = "car")
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "carSequence")
private Long id;
//...
}
It is based on the following SQL (simplified):
CREATE TABLE car (id bigint NOT NULL);
ALTER TABLE car ADD CONSTRAINT PK_car PRIMARY KEY (id);
However, I got this warning which I wanted to solve:
WARN org.hibernate.orm.deprecation: HHH90000014: Found use of deprecated [org.hibernate.id.SequenceHiLoGenerator] sequence-based id generator; use org.hibernate.id.enhanced.SequenceStyleGenerator instead. See Hibernate Domain Model Mapping Guide for details.
So, I replaced the old Car with:
import org.hibernate.annotations.GenericGenerator;
import javax.persistence.*;
#GenericGenerator(
name = "carSequence",
strategy = "org.hibernate.id.enhanced.SequenceStyleGenerator",
parameters = {
#org.hibernate.annotations.Parameter(name = "sequence_name", value = "car_id_seq"),
#org.hibernate.annotations.Parameter(name = "initial_value", value = "1"),
#org.hibernate.annotations.Parameter(name = "increment_size", value = "50")
}
)
#Table(name = "car")
public class Car {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "carSequence")
private Long id;
//...
}
However, then I got error's in my code:
Caused by: org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "pk_car"
Detail: Key (id)=(-34) already exists.
Strange thing: -34 seems odd.
Other strange thing: sequence generates duplicate keys.
The table of cars is already filled up until id = 709.
On the sequence definition, curval = 15, nextval = 16 and increment = 1.

You're not specifiying the optimizer to use.
In your first variant you're using the SequenceHiLoGenerator, in the second variant you only specify the increment-size without setting the optimizer to HiLo.
According to docs (SequenceStyleGenerator) Hibernate chooses a generator itself if not specified. Because increment_size is > 1 (see here) Hibernate probably selects the pooled-lo optimizer wich operates differently to seqHiLo.
So, you should add
#Parameter(name = "optimizer", value = "hilo"),
to your Parameters to set the optimizer explizitly to seqhilo

Related

JPA/Hibernate on Oracle: Skip autogenerated ID Column when I persist the entity

Info: Oracle DB 19. Hibernate is v5.5.4 with org.hibernate.dialect.Oracle12cDialect. JDBC driver is v12.2.0.1.
Question:
I want to save an entity with JPA/Hibernate (DB is Oracle 11g), that has an autogenerated ID column (here: PROT_ID).
CREATE TABLE SOME_PROTOCOL(
PROT_ID NUMBER(18) GENERATED ALWAYS AS IDENTITY (START WITH 123 MAXVALUE 99999) NOT NULL,
MORE_COLS VARCHAR2(500 CHAR) NOT NULL);
To add a new record, I have to skip the ID column, like that:
insert into SOME_PROTOCOL (MORE_COLS) values ('Some Value');
This is my Entity class:
#Entity
#Table(name = "SOME_PROTOCOL")
public class SomeProtocol {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "PROT_ID", insertable = false)
private Long id;
// Getter, Setter, other columns
}
Saving the entity with
SomeProtocol s = new SomeProtocol();
s.setMoreCols("whatever");
hibernateSession.save(s);
leads to this error:
ERROR: Invalid argument(s) in call
Hibernate: insert into APPL_PROTOCOL (PROT_ID, MORE_COLS) values (default, ?)
Ok, JPA doesn't skip the ID column, but sets default as a value.
I tried some more with #Column(insertable=false) or GenerationType.AUTO, but to no avail.
How can I save an entity class with an autogenerated ID column?
Solution:
We changed the ID generation for that table, we now use an external sequence (previously it was auto-generated). Hibernate can save the entity now via hibernateSession.save.
#SequenceGenerator(name = "SEQ_APPL_PROT_GENERATOR", sequenceName = "SEQ_APPL_PROTOCOL_ID", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_APPL_PROT_GENERATOR")
#Column(name = "PROT_ID")
private Long id;
I think the insertable = false in your #Column annotation might be the problem. Please try without that and let us know how that works.
You can read more about this attribute on Please explain about insertable=false and updatable=false in reference to the JPA #Column annotation.

Hibernate #SequenceGenerator return incorrect value in MS SQL

#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "generator")
#SequenceGenerator(name = "generator", schema = "MD", sequenceName = "sq_base_class")
public Long getId() {
return id;
}
Hi! I have entity which using MS SQL Sequence for generating ID, but value is incorrect.
com.microsoft.sqlserver.jdbc.SQLServerException: Violation of PRIMARY KEY constraint 'PK_BCL'. Cannot insert duplicate key in object 'MD.BASE_CLASS'. The duplicate key value is (551009).
Example: SequenceGenerator set ID = 551009, but select next value for md.sq_base_class return 551115. How to resolve it?
Hibernate-version: 5.3.10.Final
Maybe you lost the allocation size = 1 in #SequenceGenerator

JPA, HIBERNATE. How to get next value from #TableGenerator

I have an entity class with below primary key generation strategy
#Id
#Column(name = "id", nullable = false)
#TableGenerator(name = "USERGENERATOR", table = "my_sequences", pkColumnName = "sequence_name", pkColumnValue = "user_id", valueColumnName = "next_value")
#GeneratedValue(strategy = GenerationType.TABLE, generator = "USERGENERATOR")
protected Integer id;
This was working fine until a new requirement came up where I need to insert a new row using a native query. The primary key column doesn't use auto_increment because of Entity Inheritance strategy (#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)) in place.
I was wondering if there is a way to ask the table generator for the next value using the EntityManager.
I don't think you can access the #TableGenerator through any public API, but getting the next value with native SQL is easy. Just use these two queries:
-- get the current value
select next_value from my_sequences where sequence_name = 'user_id';
-- update value
update my_sequences set next_value = next_value + 1;
There is a risk of conflict if #TableGenerator fetches the next value at the same time.
You need to change your #GeneratedValue(strategy = GenerationType.TABLE, generator = "USERGENERATOR") by #GeneratedValue(strategy = GenerationType.IDENTITY) like this when you add a new element, it will add 1 auto

hibernate cannot find sequence

I need a simple sequence which would give me incremented integers that I later on use as part of a String.
I made this sequence using postgresql command line:
CREATE SEQUENCE my_seq
INCREMENT BY 1
The sequence exists as I can query it from postgresql command line, But I'm trying to get the values using hibernate:
Query query = session.createSQLQuery("select nextval(:sequence);");
query.setParameter("sequence", "my_seq");
Long nextVal=((BigInteger)query.uniqueResult()).longValue();
And I am getting this exception:
ERROR: relation "my_seq" does not exist
The sequence values do NOT represent the attribute of any entity. I only need them to store the number of logins, but I do not store the logins as entities.
EDIT: I got it working by adding the scheme name to the query:
String query1 = "select nextval(myscheme.my_seq)";
Query query = session.createSQLQuery(query1);
I can't figure out why it needed the scheme though, as myscheme was already default and all other queries worked fine without specifying the scheme. If anyone can shed some light I shall accept its answer.
You don't need to map a sequence to an entity.
Just leave the mapping as follow for the ID column of your entity, and the id of the tntity will be generated from the sequence my_seq. You don't need to call nextval(my_seq) or anything else.
#Entity("myEntity")
public class MyEntity {
#Id
#Column(name = "id", unique = true, nullable = false, insertable=false)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "my_seq")
#SequenceGenerator(name = "my_seq", sequenceName = "my_seq", allocationSize = 1)
private long id;
}
Below is the logic which will use a sequence called "my_seq" from the database and map it to the column "column_name" table "table_name"
Soon after you import the classes the sequence generator should be placed before you begin the class
#Entity
#org.hibernate.annotations.Entity(dynamicInsert = true, dynamicUpdate = true)
#Table(name = "table_name")
#SequenceGenerator(name = "my_seq", sequenceName = "my_seq")
public class ClassName implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO, generator = "my_seq")
#Column(name = "column_name")
private Long columnName;
}
Let me know if this is useful.
In order to avoid hardcoding schema to your query, you can set hibernate.default_schema property in your persistence.xml as follows.
<property name="hibernate.default_schema" value="myscheme"/>
This could be also used in spring configuration file as is stated here.

#UniqueConstraint and #Column(unique = true) in hibernate annotation

What is difference between #UniqueConstraint and #Column(unique = true)?
For example:
#Table(
name = "product_serial_group_mask",
uniqueConstraints = {#UniqueConstraint(columnNames = {"mask", "group"})}
)
And
#Column(unique = true)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private ProductSerialMask mask;
#Column(unique = true)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private Group group;
As said before, #Column(unique = true) is a shortcut to UniqueConstraint when it is only a single field.
From the example you gave, there is a huge difference between both.
#Column(unique = true)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private ProductSerialMask mask;
#Column(unique = true)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
private Group group;
This code implies that both mask and group have to be unique, but separately. That means that if, for example, you have a record with a mask.id = 1 and tries to insert another record with mask.id = 1, you'll get an error, because that column should have unique values. The same aplies for group.
On the other hand,
#Table(
name = "product_serial_group_mask",
uniqueConstraints = {#UniqueConstraint(columnNames = {"mask", "group"})}
)
Implies that the values of mask + group combined should be unique. That means you can have, for example, a record with mask.id = 1 and group.id = 1, and if you try to insert another record with mask.id = 1 and group.id = 2, it'll be inserted successfully, whereas in the first case it wouldn't.
If you'd like to have both mask and group to be unique separately and to that at class level, you'd have to write the code as following:
#Table(
name = "product_serial_group_mask",
uniqueConstraints = {
#UniqueConstraint(columnNames = "mask"),
#UniqueConstraint(columnNames = "group")
}
)
This has the same effect as the first code block.
From the Java EE documentation:
public abstract boolean unique
(Optional) Whether the property is a unique key. This is a shortcut for the
UniqueConstraint annotation at the table level and is useful for when the unique key
constraint is only a single field. This constraint applies in addition to any constraint
entailed by primary key mapping and to constraints specified at the table level.
See doc
In addition to Boaz's answer ....
#UniqueConstraint allows you to name the constraint, while #Column(unique = true) generates a random name (e.g. UK_3u5h7y36qqa13y3mauc5xxayq).
Sometimes it can be helpful to know what table a constraint is associated with. E.g.:
#Table(
name = "product_serial_group_mask",
uniqueConstraints = {
#UniqueConstraint(
columnNames = {"mask", "group"},
name="uk_product_serial_group_mask"
)
}
)
In addition to #Boaz's and #vegemite4me's answers....
By implementing ImplicitNamingStrategy you may create rules for automatically naming the constraints. Note you add your naming strategy to the metadataBuilder during Hibernate's initialization:
metadataBuilder.applyImplicitNamingStrategy(new MyImplicitNamingStrategy());
It works for #UniqueConstraint, but not for #Column(unique = true), which always generates a random name (e.g. UK_3u5h7y36qqa13y3mauc5xxayq).
There is a bug report to solve this issue, so if you can, please vote there to have this implemented. Here:
https://hibernate.atlassian.net/browse/HHH-11586
Thanks.

Categories