Postgres Partitioning not working with hibernate if id is bigserial - postgresql

I have partitioned my table in Postgres. So there are 2 tables now :
Base table users , with no primary key but using a sequence generator for id column : nextval('users_id_seq'::regclass)
Child table inheriting users
CREATE TABLE users_part_2019_01 (
CHECK (createdon >= '2019-01-01 00:00:00'
AND createdon < '2019-02-01 00:00:00')
) INHERITS (users);
ALTER TABLE users_part_2019_01 ADD CONSTRAINT users1_pkey PRIMARY KEY (id);
I am inserting data into users table using jpa. In the data model I have used :
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
Getting the following error :
"org.springframework.orm.jpa.JpaSystemException: The database returned
no natively generated identity value; nested exception is
org.hibernate.HibernateException: The database returned no natively
generated identity value
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:333)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244)"
It is working as expected independently with postgres.

Got the solution. Instead of GenerationType.IDENTITY, GenerationType.AUTO is working

In case you are coming here from spring boot (2.2.x +) and using postgresql partitioning, the accepted answer alone is not enough to make it work. Hibernate, in this case, will throw the following:
Schema-validation: missing sequence [schema_name.hibernate_sequence]
In this case, the hibernate simply wants you to provide the identity generator for the sequence field by passing it as:
#GeneratedValue(strategy = GenerationType.AUTO, generator = "schema_name.generator_sequence_name_seq")

Related

"hibernate_sequence" does not exist"

I am getting "ERROR: relation "hibernate_sequence" does not exist" exception while doing insert operation.
Technical Stack
-> Springboot
-> Hibernate
-> PostgreSQL
Approaches tried so far.
-> Verified all entity classes in project, generation strategy is used as "#GeneratedValue(strategy = GenerationType.IDENTITY)".
-> Verified database tables, pk is either Serial or Int with proper sequence generated value.
-> Tried with use-new-id-generator-mappings property as false, didn't worked.
-> Verified sequence with name "hibernate_sequence" is available in Database.
Analysis so far
-> Entities those are annotated with #Audited having this issue as hibernate envers expect global "hibernate_sequence". But not able to find the exact solution.
Note : This was working few days back without any issue, Since last week started getting this issue.
As you said, hibernate-envers is looking for the hibernate_sequence.
Its used to insert records into the REVINFO table
Assuming spring.jpa.hibernate.ddl-auto is not set to create
either
create a hibernate_sequence manually
create a sequence with the name you want. e.g rev_id_seq. Then override the REVINFO definition to change the sequence name by adding your definition of the RevisionEntity
#Entity
#RevisionEntity
public class MyRevision implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "rev_id_generator")
#SequenceGenerator(name = "rev_id_generator", sequenceName = "rev_id_seq", allocationSize = 1)
#RevisionNumber
private int id;
#RevisionTimestamp
private long timestamp;
// Getters, setters, equals, hashCode ...
}
https://docs.jboss.org/hibernate/orm/current/userguide/html_single/Hibernate_User_Guide.html#envers-tracking-modified-entities-revchanges
https://thorben-janssen.com/hibernate-envers-extend-standard-revision/
Initially set the spring.jpa.hibernate.ddl-auto to create for the first time and run the application. It will create the hibernate sequence. after that change spring.jpa.hibernate.ddl-auto to none. It will prevent any further data loss from tables. Or you can set to update if necessary.
Because you are using #GeneratedValue()
It will look for how the database that you are using generates ids. For MySql or HSQSL, there are increment fields that automatically increment. In Postgres or Oracle, they use sequence tables. Since you didn't specify a sequence table name, it will look for a sequence table named hibernate_sequence and use it for default. So you probably don't have such a sequence table in your database and now you get that error.
I Also got this working using;
#GeneratedValue(strategy = GenerationType.IDENTITY)
I faced the exact same issue when I migrated from Maria to Postgres. Either/both problems one may have:
Schema name is not in the connection URL
If the schema name isn't passed flyway_schema_history table and sequences were created under the public schema. And application tables were made under the custom schema.
So make sure you have the required schema configured.
spring:
datasource:
url: jdbc:postgresql://localhost:5432/platform?currentSchema=product1
username: admin
password: admin
driver-class-name: org.postgresql.Driver
flyway:
schemas:
- product1
Sequence got created but with another name
This was the problem for me. Sequence got created with the name revinfo_rev_seq. However, while inserting the records it was looking for hibernate_sequence.
I added another revision under flyway migration to rename the already created sequence.
-- This is not required for MySQL/MariaDB. However, while using PostgresSQL getting the error -> ERROR: relation "hibernate_sequence" does not exist
ALTER SEQUENCE revinfo_rev_seq RENAME TO hibernate_sequence;

Use Sequence created by Flyway in JPA

I'm using a Spring Boot 2 / Flyway / Postgres setup.
I want to achieve to let Flyway create a table with a sequence for automatic key iteration. JPA should recognize the sequence and use it.
I let Flyway execute a PostgreSQL script:
CREATE SEQUENCE config_id_seq;
CREATE TABLE config
(
ID BIGINT NOT NULL PRIMARY KEY DEFAULT nextval('config_id_seq'),
DESCRIPTION VARCHAR(500)
);
And this is the Entity definition:
#Entity
#Table(name = "config")
public class Config {
#Id
#SequenceGenerator(name = "config_id_sequence", sequenceName = "config_id_seq")
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "config_id_sequence")
#Column(name = "id")
private long id;
#Column(name = "description")
private String description;
On start up the following errors are thrown:
Caused by: org.postgresql.util.PSQLException: ERROR: relation "config_id_seq" already exists
Caused by: org.postgresql.util.PSQLException: ERROR: cannot change sequence "config_id_seq"
My interpretation is that Flyway successfully executed the script and created a sequence. But JPA wants to create the sequence afterwards and fails because it already exists. Please, correct me if I'm wrong here.
Now how can I configure JPA to reuse the existing sequence, if this is possible?
We need to set spring.jpa.hibernate.ddl-auto property to none or you can skip this property so that spring does not create database objects on its own.
If we are using a flyway then we should give the responsibility of database objects creation to flyway only i.e create all database objects with flyway scripts only like tables and sequence.
Specifying GeneratedValue says for JPA to use a sequence, retrieve the next value itself, and then use that in the INSERT.
But make sure you have the same configuration that you mentioned on entity class and flyway script.

Insert into postgres table with select and autoincrement

I want to move data from a table T1 to another table T2. T1 has an autoincrement id, which is consequente of the JPA annotations #Id #GeneratedValue(strategy = GenerationType.SEQUENCE) with Spring Data implementation.
insert into T1(id, dataColumn)
(select NULL, dataToCopy
from T2)
This doesn't work, as it reports an error on the violation of the non-null constraint on the id field. How can I insert my data in this case?
The reason I cannot make the insert that way is that autoincrement is not set on the id column. The reason for not having autoincrement is that setting the a generator for the id leads to the need of making an additional select statements to get the next value from a database sequence.

JPA 2 #JoinTable with keygeneration

Is there a way in JPA 2 to use a #JoinTable to generate a UUID key for the id of the row? I do not want to create new entity for this table (even if that would solve the problem) and I do not want to create it from the DB.
#ManyToMany
#JoinTable(name="Exams_Questions", schema="relation",
joinColumns = #JoinColumn(name="examId", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="questionId", referencedColumnName = "id"))
private List<Question> questions = new ArrayList<Question>();
db table
CREATE TABLE [relation].[Exams_Questions](
[id] [uniqueidentifier] PRIMARY KEY NOT NULL,
[examId] [uniqueidentifier] NOT NULL,
[questionId] [uniqueidentifier] NOT NULL,
Not sure exactly what the question is, but let me try a response.
For your first sentence alone, I would say "Yes" and "Possibly":
You'll need a separate #Entity class for the Question, and in that class you'd specify the mapping for id.
There is no way using spec JPA to specify auto-generation of a UUID value for a column. There are ways using OpenJPA and Hibernate. EclipseLink will allow you to create a custom generator for this purpose, and their example is, in fact, for a UUID.
If you'd like to expose properties of the join-table OR otherwise have JPA manage them (i.e. the id on the Exams_Questions table), then see this external link (found on this answer). You'll end up with #OneToMany relations from Exam/Question entities to the join table, and #ManyToOne relations from the join table to Exam/Question entities.
Exposing the join table as an entity will let you manage a separate key (uuid). If you don't need the uuid primary key, then don't do this - it's not necessary to solve the problem, as the examId/questionId combination is unique.

Meaning of #GeneratedValue with strategy of TABLE

The JPA specification gives the following explanation of the annotation #GeneratedValue(strategy=TABLE):
The TABLE generator type value indicates that the persistence provider must assign primary keys for the entity using an underlying database table to ensure uniqueness.
But what does "using an underlying database table" mean in practice? Does it mean using an auxiliary table? Or by scanning the entity-table to find an ID not in use? Or something else?
Check out JavaDoc for TableGenerator, it has a nice example of how it works:
Example 1:
#Entity public class Employee {
...
#TableGenerator(
name="empGen",
table="ID_GEN",
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="EMP_ID",
allocationSize=1)
#Id
#GeneratedValue(strategy=TABLE, generator="empGen")
int id;
...
}
Example 2:
#Entity public class Address {
...
#TableGenerator(
name="addressGen",
table="ID_GEN",
pkColumnName="GEN_KEY",
valueColumnName="GEN_VALUE",
pkColumnValue="ADDR_ID")
#Id
#GeneratedValue(strategy=TABLE, generator="addressGen")
int id;
...
}
Basically ID_GEN is an internal (non-business) table of key-value pairs. Every time JPA wants to generate ID it queries that database:
SELECT GEN_VALUE
FROM ID_GEN
WHERE GEN_KEY = ...
and incremenets the GEN_VALUE column. This mechanism can be used to emulate sequences or to take even further control of generated ids.
In the case of EclipseLink, it uses an auxiliary table. The documentation says
By default, EclipseLink chooses the TABLE strategy using a table named SEQUENCE, with SEQ_NAME and SEQ_COUNT columns