#UniqueConstraints not working on #JoinTable - jpa

I'm using Playframework 1.2.4 and PostgresSQL 9.1.2. I have the following entities: Recipe and RecipeItem. A Recipe has a set of RecipeItems. I've annotated the set of recipe items in the Recipe class as follows:
#Required
#MinSize(1)
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinTable( name = "RecipeItemForIngredients",
joinColumns = #JoinColumn(name = "recipeId"),
inverseJoinColumns = #JoinColumn(name = "recipeItemId"),
uniqueConstraints = #UniqueConstraint(name = "uq_recipeItemPerRecipe",
columnNames = {"recipeId", "recipeItemId"}))
public Set<RecipeItem> items = Sets.newHashSet();
But when I check the PgAdmin to see if the constraint has been applied to the RecipeItemForIngredients table I cannot find it. This is what PgAdmin shows.
CREATE TABLE recipeitemforingredients
(
recipeid bigint NOT NULL,
recipeitemid bigint NOT NULL,
CONSTRAINT recipeitemforingredients_pkey PRIMARY KEY (recipeid, recipeitemid),
CONSTRAINT fk5ac547a883708db FOREIGN KEY (recipeid)
REFERENCES recipe (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk5ac547ad6e1da8f FOREIGN KEY (recipeitemid)
REFERENCES "recipe$recipeitem" (id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT recipeitemforingredients_recipeitemid_key UNIQUE (recipeitemid)
)
Does anyone have an idea why this could be happening? Maybe this annotation is not supported by the ORM used by Playframework.

This constraint doesn't make much sense, and you have a stronger constraint by default anyway:
CONSTRAINT recipeitemforingredients_recipeitemid_key UNIQUE (recipeitemid)
If recipeItemId is unique, then of course, the tuple (recipeId, recipeItemId) is also unique.
Moreover, since the PK of the table is (recipeId, recipeItemId), the constraint is already applied by the PK constraint.

Related

org.postgresql.util.PSQLException: ERROR: update or delete on table "skills" violates foreign key constraint on table "employee_has"

I am developing a CRUD application using Angular, SpringBoot and PostgreSQL. There I have two tables named "skills" and "employees" in the database. The table created by joining both "skills" and "employee" tables is "employee_has" table. They are mapped using Many-to-Many relationship. An employee can have many skills. A skill can have many employees.
I need the functionality as when I delete a skill in the "skill" table, it should be removed from employees' who have that skill. But When I delete a skill, it does not getting deleted form the skill table and the relationship does not get deleted in the "employee_has" table and gives the below error.
org.postgresql.util.PSQLException: ERROR: update or delete on table "skills" violates foreign key constraint "fkoq05nk3xfqd4rl68fdpt17vvc" on table "employee_has"
Detail: Key (id)=(5) is still referenced from table "employee_has".
Here is my code part for Skill model in the backend.
public class Skill {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#NotNull
#NaturalId
private String skill_name;
#JsonIgnore
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
},
mappedBy = "skills")
private List<Employee> employees = new ArrayList<>();
Here is my code part for Employee model in the backend.
#ManyToMany(fetch = FetchType.LAZY,
cascade = {
CascadeType.PERSIST,
CascadeType.MERGE
})
#JoinTable(name = "employee_has",
joinColumns = { #JoinColumn(name = "emp_id") },
inverseJoinColumns = { #JoinColumn(name = "skill_id") })
private List<Skill> skills = new ArrayList<>();
Please help me to solve this issue.
This error occurs because you are trying to delete a "skill" that is a reference to the join table ("employee_has"). This can fix by the database side.
Instead of using,
PostgreSQL create script
CREATE TABLE IF NOT EXISTS public.employee_has
(
employee_id bigint NOT NULL,
skill_id bigint NOT NULL,
CONSTRAINT employee_has_pkey PRIMARY KEY (employee_id, skill_id),
CONSTRAINT fkam2psf41jwoy33ge3uvxep8tl FOREIGN KEY (skill_id)
REFERENCES public.skill (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE NO ACTION,
CONSTRAINT fkkd8xx37dlmjryoas0d91hri6c FOREIGN KEY (employee_id)
REFERENCES public.employee (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
)
Use this- Replacing "ON DELETE NO ACTION" to "ON DELETE CASCADE",
Fix it by replacing No Action to Cascade
CREATE TABLE IF NOT EXISTS public.employee_has
(
employee_id bigint NOT NULL,
skill_id bigint NOT NULL,
CONSTRAINT employee_has_pkey PRIMARY KEY (employee_id, skill_id),
CONSTRAINT fkam2psf41jwoy33ge3uvxep8tl FOREIGN KEY (skill_id)
REFERENCES public.skill (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE,
CONSTRAINT fkkd8xx37dlmjryoas0d91hri6c FOREIGN KEY (employee_id)
REFERENCES public.employee (id) MATCH SIMPLE
ON UPDATE NO ACTION
ON DELETE CASCADE
)

JPA - Cascade Delete failing

I have Table A ...
id:bigint - primary key NOT NULL
name:string
... and i have Table B that has a foreign key to table A ...
id:bigint - primary key NOT NULL
name:string
a_id:bigint - foreign key to table A.id NOT NULL
When i generate the jpa entities and jpacontrollers the foreign key looks like this in Table A entity
#OneToMany(mappedBy = "aId", cascade = CascadeType.ALL)
private List<B> bList;
and it looks like this in table B entity
#JoinColumn(name = "a_id", referencedColumnName = "id")
#ManyToOne(optional = false)
private A aId;
However when delete an entry from table A that has a linked entry in table B that should be cascade deleted, it hits an error in the table A jpacontroller destroy method complaining that it cannot remove the a_id from table B as the field is not null (i.e. it is trying to remove the link but preserve the record rather than cascade delete). The code in the destroy method in the jpa controller where it hits this error is here
List<B> bList = a.getBList();
for (B bListB : bList) {
bListB.setAId(null);
bListB = em.merge(bListB);
}
if i remove that code, the cascade delete works, but when the jpa controller is next regenerated i will lose my changes.
could someone advise what am i doing wrong here please.
thank you.

Use postgres table sequence instead of sharing hibernate_sequence

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));

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.

Java EE 6 JPA 2 ManyToOne Relation Creates Invalid Foreign Key

I am trying to create two entities where both entities have embeddedIds. One of the entities have 2 references to the other entity, where both of those references are related as ManyToOne.
Example codes are written below;
#Embeddable
public class ItemPK {
#Column(nullable = false, length = 100)
private String itemId;
#Column(name = "item_client_id", nullable = false)
private int clientId;
...
}
#Entity
#Table(name = "item")
public class Item {
#EmbeddedId
private ItemPK id;
#ManyToOne
#JoinColumn(name = "item_client_id")
private Client client;
#OneToMany(mappedBy="item", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<RelatedItem> relatedItems;
#OneToMany(mappedBy="relatedItem", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<RelatedItem> relatedItemsRHS;
...
}
#Embeddable
public class RelatedItemPK {
#Column(name = "itemId", length = 100, nullable = false)
private String itemId;
#Column(name = "item_client_id", nullable = false)
private int clientId;
#Column(name = "relatedItemId", length = 100, nullable = false)
private String relatedItemId;
#Column(name = "related_item_client_id", nullable = false)
private int relatedItemClientId;
...
}
#Entity
#Table(name = "related_item")
public class RelatedItem {
#EmbeddedId
private RelatedItemPK id;
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumns({
#JoinColumn(name="itemId", referencedColumnName="itemId", insertable=false, updatable=false),
#JoinColumn(name="item_client_id", referencedColumnName="item_client_id", insertable=false, updatable=false)
})
private Item item;
#ManyToOne(cascade = CascadeType.ALL, optional = false)
#JoinColumns({
#JoinColumn(name="related_item_client_id", referencedColumnName="item_client_id", insertable=false, updatable=false),
#JoinColumn(name="relatedItemId", referencedColumnName="itemId", insertable=false, updatable=false)
})
private Item relatedItem;
...
}
The problem is while creating foreign keys for RelatedItem entity, I got an SQLException. It is the second ManyToOne relation that fails. The foreign key generation sql is below,
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_related_item_client_id FOREIGN KEY (related_item_client_id, relatedItemId) REFERENCES item (item_client_id, itemId)
Since item table is indexed first by itemId then by item_client_id, this statement causes MySQL to produce an error.
I would like to switch the places of columns so that the SQL should look like the following,
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_relatedItemId FOREIGN KEY (relatedItemId, related_item_client_id) REFERENCES item (itemId,item_client_id)
I tried changing the order of "JoinColumn"s but the result didn't change. I also tried renaming the fields to check if persistence provider choses the order by column name but again the result didn't change.
So, is there a way to enforce the column ordering?
p.s. I use following stuff:
MySQL 5.1
EclipseLink 2.0.0
Java EE 6
JPA 2
GlassFish v3
Edit: EclipseLink produces following SQL, which fails to run;
CREATE TABLE related_item (SIMILARITY DOUBLE, widget_id INTEGER NOT NULL, relatedItemId VARCHAR(100) NOT NULL, itemId VARCHAR(100) NOT NULL, related_item_client_id INTEGER NOT NULL, item_client_id INTEGER NOT NULL, PRIMARY KEY (widget_id, relatedItemId, itemId, related_item_client_id, item_client_id));
CREATE TABLE item (IMAGEURL VARCHAR(2048), STATUS VARCHAR(64), URL VARCHAR(2048), PRICE DOUBLE, STOCK INTEGER, DESCRIPTION TEXT(64000), NAME VARCHAR(255), ITEMID VARCHAR(100) NOT NULL, item_client_id INTEGER NOT NULL, PRIMARY KEY (ITEMID, item_client_id));
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_itemId FOREIGN KEY (itemId, item_client_id) REFERENCES item (itemId, item_client_id);
ALTER TABLE related_item ADD CONSTRAINT FK_related_item_related_item_client_id FOREIGN KEY (related_item_client_id, relatedItemId) REFERENCES item (item_client_id, itemId);
ALTER TABLE item ADD CONSTRAINT FK_item_item_client_id FOREIGN KEY (item_client_id) REFERENCES client (ID);
Please include the stack trace. However, I strongly recommend you skip the #JoinColumn tags unless you have a VERY good reason for specifying the foreign keys yourself. By specifying the mappedBy attribute in one of the directions, JPA can figure out what to do by itself.
Java EE 6 and JPA put a lot of effort into enabling Convention over Configuration, which means that most of the time, things will work out of the box. It's desirable for you, the programmer because you have less boiler plate code to worry about, and it's desirable for the JPA and Jave EE container implementors because it gives them freedom to chose the best performing solutions. By declaring the foreign key relationships yourself, you rob both you and JPA of this advantage.
Edit: In fact, I suspect that both specifying mappedBy and specifying the #JoinTable could be the root cause of your problem. But I need to see the stack trace to tell for sure.
The order of the columns should not matter. If it does, then you could change the order in your index to match, or change the order you list your primary key in, or just use your scripts to generate your DDL.