Use postgres table sequence instead of sharing hibernate_sequence - postgresql

When I do anything with a table, it always show the error:
Hibernate: select nextval ('hibernate_sequence')
2019-07-20 16:15:44.877 WARN 58376 --- [nio-9000-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 0, SQLState: 42P01
2019-07-20 16:15:44.877 ERROR 58376 --- [nio-9000-exec-1] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "hibernate_sequence" does not exist
I DON'T want to use hibernate_sequence to share id sequence between tables, but want to define id seq for each table and use them respectively.
I use Spring Boot 2.1.6.RELEASE, Spring Data JPA (Hibernate 5.3.10.Final), and Postgres 11.2, and define the id field with BigSerial type and hope to use the id sequence of each table in respective entity class.
The demo repo is here: https://github.com/Redogame/share_hibernate_sequence
Create user table (use identity as table name because user is a Postgres reserved keyword).
By defining id with bigserial type, Postgres will create a identity_id_seq automatically, and I verified that identity_id_seq has been created successfully.
create table identity
(
id bigserial not null
constraint identity_pkey
primary key,
name varchar(255) not null
constraint identity_name_key
unique
constraint identity_name_check
check ((name)::text <> ''::text),
created_date timestamp not null,
created_by_id bigint not null
constraint identity_identity_id_fk
references identity,
last_modified_date timestamp not null,
last_modified_by_id bigint not null
constraint identity_identity_id_fk_2
references identity,
version bigint not null
);
Specify a sequence generator to use this id sequence:
#Table(name = "identity")
public class UserEntity extends Auditable<Long> {
#Id
#SequenceGenerator(name="identity_id_seq", sequenceName = "identity_id_seq", initialValue=1, allocationSize=1)
#GeneratedValue(strategy=GenerationType.SEQUENCE, generator="identity_id_seq")
private Long id;
But it doesn't work. I also tried to config spring.jpa.hibernate.use-new-id-generator-mappings and spring.jpa.properties.hibernate.id.new_generator_mappings, but still not work.
spring:
jpa:
hibernate:
use-new-id-generator-mappings: false
properties:
hibernate:
id:
new_generator_mappings: false
I expect not to use hibernate_sequence, that is: don't execute select nextval ('hibernate_sequence') before/after any SQL statement.

Try the below steps
CREATE SEQUENCE IF NOT EXISTS manual_seq;
Change create table script
create table identity
(
id integer NOT NULL DEFAULT nextval('manual_seq'::regclass),
name varchar(255) not null
constraint identity_name_key
unique
constraint identity_name_check
check ((name)::text <> ''::text),
created_date timestamp not null,
created_by_id bigint not null,
last_modified_date timestamp not null,
last_modified_by_id bigint not null,
version bigint not null,
CONSTRAINT manual_seq_pkey PRIMARY KEY (id)
);
I removed the foreign key constraint for testing purposes.
Update the entity mapping
#Entity
#Table(name = "identity")
#JsonIgnoreProperties(ignoreUnknown = true)
public class UserEntity extends Auditable<Long> {
#Id
#SequenceGenerator(name="manual-seq", sequenceName = "manual_seq",allocationSize = 1)
#GeneratedValue(generator="manual-seq")
private Long id;
#Basic
#Column(name = "name", nullable = false)
private String name;
#MappedSuperclass
#JsonIgnoreProperties({"new", "createdDate", "createdById", "lastModifiedDate", "lastModifiedById", "version"})
abstract class Auditable<PK extends Serializable>{
#NotAudited
#CreatedDate
#Temporal(TemporalType.TIMESTAMP)
private Date createdDate;
#NotAudited
#CreatedBy
private Long createdById;
#LastModifiedDate
#Temporal(TemporalType.TIMESTAMP)
private Date lastModifiedDate;
#LastModifiedBy
private Long lastModifiedById;
#NotAudited
#Version
private Long version;
Revert the spring.jpa.hibernate.use-new-id-generator-mappings
The issue was extending AbstractPersistable because of which database sequence was not getting used. Also, note I have removed the audit for testing purposes.

The same problem was happening to me. I explicitly set spring.jpa.properties.hibernate.id.new_generator_mappings=false but select nextval ('hibernate_sequence') was still run by Hibernate.
I found that when we use the #GeneratedValue annotation without setting a strategy, it defaults to AUTO, which means, Hibernate will try to generate the ID value using hibernate_sequence and then it wil fail because it doesn't exist in the database.
So, I made #GeneratedValue (strategy = GenerationType.IDENTITY) and tried again. In this case, the ID value was generated by my identity column in the database (the primary key that is automatically incremented) and not by hibernate_sequence.
create table users (id serial not null, name varchar(250), primary key (id));

Related

Generate "GENERATED ALWAYS AS IDENTITY" With Spring JPA for PostgreSQL

Is it possible to get Spring-Data-JPA to generate PRIMARY KEY column with GENERATED ALWAYS AS IDENTITY.
class SomeClass {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private ID id;
...
}
The result obtained with javax.persistence.* library is:
CREATE TABLE IF NOT EXISTS public.sometable
(
id bigint NOT NULL DEFAULT nextval('sometable_id_seq'::regclass),
...
CONSTRAINT sometable_pkey PRIMARY KEY (id)
...
);
However, the ideal code would be:
CREATE TABLE IF NOT EXISTS public.sometable (
id bigint PRIMARY KEY GENERATED ALWAYS AS IDENTITY
...
);
Is there a directive to tell Spring Data to generate GENERATED ALWAYS AS IDENTITY with PostgreSQL?
No, it’s not possible. Have tested with columnDefinition, and it either produces an exception or an invalid DDL. Have a nice day :)

ERROR: relation "hibernate_sequence" does not exist

I do not want to use autoincrement id. I have this table in PostgreSQL:
CREATE TABLE public.project
(
id character varying(255) COLLATE pg_catalog."default" NOT NULL,
name character varying(255) COLLATE pg_catalog."default" NOT NULL,
number bigint NOT NULL,
state character varying(255) COLLATE pg_catalog."default",
CONSTRAINT project_pkey PRIMARY KEY (id)
)
And I have this model:
#Entity
#Table(name = "project")
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Project {
#Id
#Column(name = "id")
private String id;
#Column(name = "name")
private String name;
#Column(name = "number")
private BigInteger number;
#Column(name = "state")
private String state;
I want to use simple ID I do not want to use autoincrement and hibernate_sequence, and do not understand where I wrong.
And I have error:
ERROR 11088 --- [ restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper : ERROR: relation "hibernate_sequence" does not exist
In our entity you should use #GeneratedValue(strategy = GenerationType.IDENTITY) on your id especially if your table will autoincrement id.
In my case I could solve the problem by upgrading the postgres driver to a more recent version which seems to work with different (older) postgres versions as well. (updated version in build.gradle):
implementation 'org.postgresql:postgresql:42.2.18'
You need to set spring.jpa.properties.hibernate.id.new_generator_mappings=false in your application.properties file.
Run:
CREATE SEQUENCE hibernate_sequence START 1;

Error in Sequence lookup in Hibernate for Postgresql

I am having problem in inserting data into Postgresql through Hibernate when Primary column has auto-increment.
I have gone through multiple post in this forum also, but can't find a solution that works for me.
My Table and Sequence are defined in "apiprofile" schema. When I run the code, it is unable to find the Sequence name.
Even if I mention it with the schema.sequence, still it doesn't work.
Any help is appreciated.
Below are the code snippet and exception I am facing.
Sequence & Table:
CREATE SEQUENCE apiprofile.login_session_id_seq;
CREATE TABLE apiprofile.login_session (
id bigint NOT NULL DEFAULT nextval('apiprofile.login_session_id_seq'),
username varchar(255) NOT NULL,
token varchar(500) NOT NULL,
active_ind boolean NOT NULL,
login_time timestamp NOT NULL,
PRIMARY KEY (id)
);
Bean file
public class LoginSession {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "session_generator")
#SequenceGenerator(name="session_generator", sequenceName = "apiprofile.login_session_id_seq", schema = "apiprofile", allocationSize=1)
#Column(name="id", updatable = false, nullable = false)
private Integer id;
Insert operation
LoginSession session = new LoginSession();
session.setUsername(userName);
session.setToken(token);
session.setActive(true);
session.setLoginTime(new Timestamp(System.currentTimeMillis()));
getSession().saveOrUpdate(session);
Exception
Hibernate:
select
next_val as id_val
from
login_session_id_seq for update
could not read a hi value
org.postgresql.util.PSQLException: ERROR: relation "login_session_id_seq" does not exist
Position: 32
at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2103) ~[postgresql-9.1-901-1.jdbc4.jar:?]
at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1836) ~[postgresql-9.1-901-1.jdbc4.jar:?]
at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:257) ~[postgresql-9.1-901-1.jdbc4.jar:?]

How exactly work the #OneToMany JPA annotation in this example? Is it related to a table column or an entity class field?

I have 2 DB tables named respectivelly T_ACCOUNT and T_ACCOUNT_BENEFICIARY.
These tables have the following structure:
create table T_ACCOUNT (ID integer identity primary key, NUMBER varchar(9), NAME varchar(50) not null, CREDIT_CARD varchar(16), unique(NUMBER));
create table T_ACCOUNT_BENEFICIARY (ID integer identity primary key, ACCOUNT_ID integer, NAME varchar(50), ALLOCATION_PERCENTAGE decimal(5,2) not null, SAVINGS decimal(8,2) not null, unique(ACCOUNT_ID, NAME));
And the T_ACCOUNT table is bound to the T_ACCOUNT_BENEFICIARY table with a one to many relationship, this is the graphical representation:
So this is the first class named Account that map the T_ACCOUNT table:
#Entity
#Table(name="T_ACCOUNT")
public class Account {
#Id
#GeneratedValue
#Column(name="id")
private Long entityId;
#Column(name="NUMBER")
private String number;
#Column(name="NAME")
private String name;
#OneToMany
#JoinColumn(name="ACCOUNT_ID")
private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();
#Column(name="CREDIT_CARD")
private String creditCardNumber;
// GETTERS & SETTERS
}
And this is the Beneficiary class that map the T_ACCOUNT_BENEFICIARY table:
/**
* A single beneficiary allocated to an account. Each beneficiary has a name (e.g. Annabelle) and a savings balance
* tracking how much money has been saved for he or she to date (e.g. $1000).
*/
#Entity
#Table(name="T_ACCOUNT_BENEFICIARY")
public class Beneficiary {
#Id
#GeneratedValue
#Column(name="ID")
private Long entityId;
#Column(name="NAME")
private String name;
#Embedded
#AttributeOverride(name="value",column=#Column(name="ALLOCATION_PERCENTAGE"))
private Percentage allocationPercentage;
#Embedded
#AttributeOverride(name="value",column=#Column(name="SAVINGS"))
private MonetaryAmount savings = MonetaryAmount.zero();
As you can see into the Account I have the beneficiaries field that implement the one to may relationship
#OneToMany
#JoinColumn(name="ACCOUNT_ID")
private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();
I know that, on the DB, this relationship is implemented by the ACCOUNT_ID field of the T_ACCOUNT_BENEFICIARY table (so multiple row of the T_ACCOUNT_BENEFICIARY table can have the same value of the ACCOUNT_ID field and this means that a single row of the T_ACCOUNT table can be associated to more than one rows of T_ACCOUNT_BENEFICIARY table).
As you can see in the previous sippet there is the #JoinColumn(name="ACCOUNT_ID") annotation.
My doubt is generated by the fact that I have an ACCOUNT_ID column on my T_ACCOUNT_BENEFICIARY table, infact:
create table T_ACCOUNT_BENEFICIARY (ID integer identity primary key, ACCOUNT_ID integer, NAME varchar(50), ALLOCATION_PERCENTAGE decimal(5,2) not null, SAVINGS decimal(8,2) not null, unique(ACCOUNT_ID, NAME));
but this column seems to not be mapped on the Beneficiary that map this T_ACCOUNT_BENEFICIARY table.
So my doubts is: the #JoinColumn(name="ACCOUNT_ID") is working at relational level performing the join operation on the ACCOUNT_ID column of the table mapped by the Beneficiary entity (T_ACCOUNT_BENEFICIARY) or am I missing something? How exactly is performed this join?
If my interpretation is right can I work at entity level and say to join the beneficiaries field of my Account entity class to a new accountId field inserted into my Beneficiary entity class and mapping the ACCOUNT_ID column of the T_ACCOUNT_BENEFICIARY table?
Tnx
It seems is a Unidirectional OneToMany relationship
In JPA 2.0 a #JoinColumn can be used on a OneToMany to define the foreign key
I'm not sure if I understand your question. But what you have done with your #JoinColumn annotation is correct and Hibernate will execute appropriate SQL statements when you have multiple beneficiaries for your account. For example executing multiple INSERTS if you have two Beneficiaries for an Account. And yes using the #JoinColumn annotation is at the hibernate level. If you want to access an Account from a Beneficiary entity you would need to define a Bidirectional relationship in the Beneficiary class like below.
#Entity
#Table("T_ACCOUNT_BENEFICIARY")
public class Beneficiary {
#ManyToOne(mappedBy = "beneficiaries")
Account account;
...
}

Why am I getting "Foreign key constraint fails" exception on persist?

Good day, please advise why am I getting following exception. I'm EclipseLink beginner, I'm using jdk1.7.0_05, MySQL server 5.5.25a.
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`stats`.`version`, CONSTRAINT `fk_version_activity1` FOREIGN KEY (`activity_id`, `activity_license_id`) REFERENCES `activity` (`id`, `license_id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
Error Code: 1452
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.3.0.v20110604-r9504): org.eclipse.persistence.exceptions.DatabaseException
Internal Exception: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint fails (`stats`.`version`, CONSTRAINT `fk_version_activity1` FOREIGN KEY (`activity_id`, `activity_license_id`) REFERENCES `activity` (`id`, `license_id`) ON DELETE NO ACTION ON UPDATE NO ACTION)
Error Code: 1452
Call: INSERT INTO version (version, activity_id, product, activity_license_id) VALUES (?, ?, ?, ?)
bind => [4 parameters bound]
Query: InsertObjectQuery(cz.ryvo.stats.database.Version[ versionPK=cz.ryvo.stats.database.VersionPK[ product=AP, activityId=0, activityLicenseId=0 ] ])
at org.eclipse.persistence.internal.jpa.EntityManagerImpl.flush(EntityManagerImpl.java:786)
at cz.audatex.audaupdateloader.DatabaseHelper.registerActivity(DatabaseHelper.java:104)
... 3 more
Table License looks like this:
CREATE TABLE `license` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`license` varchar(20) NOT NULL,
`organisation_id` int(11) DEFAULT NULL,
`user_id` int(11) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `license_UNIQUE` (`license`),
KEY `fk_license_user1` (`user_id`),
KEY `fk_license_organisation1` (`organisation_id`),
CONSTRAINT `fk_license_organisation1` FOREIGN KEY (`organisation_id`) REFERENCES `organisation` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_license_user1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=latin1;
Table Activity:
CREATE TABLE `activity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`license_id` int(11) NOT NULL,
`time` datetime NOT NULL,
`type` char(1) NOT NULL,
`result` int(1) DEFAULT NULL,
PRIMARY KEY (`id`,`license_id`),
UNIQUE KEY `activity_UQ01` (`license_id`,`time`,`type`),
KEY `fk_activity_license1` (`license_id`),
CONSTRAINT `fk_activity_license1` FOREIGN KEY (`license_id`) REFERENCES `license` (`id`) ON DELETE CASCADE ON UPDATE NO ACTION
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=latin1;
And table Versions:
CREATE TABLE `version` (
`product` varchar(45) NOT NULL,
`activity_id` int(11) NOT NULL,
`activity_license_id` int(11) NOT NULL,
`version` varchar(45) DEFAULT NULL,
PRIMARY KEY (`product`,`activity_id`,`activity_license_id`),
KEY `fk_version_activity1` (`activity_id`,`activity_license_id`),
CONSTRAINT `fk_version_activity1` FOREIGN KEY (`activity_id`, `activity_license_id`) REFERENCES `activity` (`id`, `license_id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
My classes I generated in NetBeans using New->Entity Classes from database look like this:
Class Version:
#Entity
#Table(name = "version")
#XmlRootElement
public class Version implements Serializable {
private static final long serialVersionUID = 1L;
#EmbeddedId
protected VersionPK versionPK;
#Column(name = "version")
private String version;
#JoinColumns({
#JoinColumn(name = "activity_id", referencedColumnName = "id", insertable = false, updatable = false),
#JoinColumn(name = "activity_license_id", referencedColumnName = "license_id", insertable = false, updatable = false)})
#ManyToOne(optional = false, fetch = FetchType.LAZY)
private Activity activity;
...
Class VersionPK:
#Embeddable
public class VersionPK implements Serializable {
#Basic(optional = false)
#Column(name = "product")
private String product;
#Basic(optional = false)
#Column(name = "activity_id")
private int activityId;
#Basic(optional = false)
#Column(name = "activity_license_id")
private int activityLicenseId;
...
I'm gettin the exception when I execute following code:
...
ActivityPK apk = new ActivityPK();
apk.setLicenseId(activity.getLicense().getId());
activity.setActivityPK(apk);
em.persist(activity);
em.flush(); <- Here the exception is thrown
...
I suspect it is because activity does not have ID assigned yet and VerionPK uses this ID. Am I right? What is the proper way to persist such data? Should I persist version collection separately after persisting activity with version collection set to null?
Many thanks in advance. Vojtech
Check out JPA 2.0's derived Ids. You can mark the relationship as the ID, or specify that the relationship the id field using maps Id instead of having to manually set the value yourself. This takes care of the problem when creating a new tree and the root entity uses sequencing - the ids aren't available to the children to use as foreign keys until the root is persisted.
Otherwise, the root entity needs to be persisted and flushed so that an Id is assigned. In this case, the Activity entity needs primary key values before you can try and persist a new Version object that reference it since you must manually set all the version.versionpk values.