ERROR: update or delete on table "tablename" violates foreign key constraint - postgresql

I'm trying to delete the parent student or parent course and I get this error:
Caused by: org.postgresql.util.PSQLException: ERROR: update or delete on table "student" violates foreign key constraint "fkeyvuofq5vwdylcf78jar3mxol" on table "registration"
RegistrationId class is a composite key used in Registration class. I'm using Spring data jpa and spring boot.
What am I doing wrong? I know that putting cascadetype.all should also remove the children when the parent is deleted but it is giving me an error instead.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OneToOne(cascade=CascadeType.ALL)
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;
Registration class
#Entity(name = "Registration")
#Table(name = "registration")
public class Registration {
#EmbeddedId
private RegistrationId id;

When you're using a relational DB, you are setting entities with relationships between these entities.
The error that you're getting means that:
You're trying to delete a record that its primary key is functioning as a foreign key in another table, thus you can't delete it.
In order to delete that record, first, delete the record with the foreign key, and then delete the original that you wanted to delete.

I made it work by using hibernate #OnDelete annotation. Some how the JPA.persistence CascadeTypes were not working. They had no effect for whichever I chose.
Just like below. Now I can remove the parent Student or the parent Course and all children(Registrations) are deleted with them.
#Embeddable
public class RegistrationId implements Serializable {
#JsonIgnoreProperties("notifications")
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "student_pcn", referencedColumnName="pcn")
private Student student;
#JsonIgnoreProperties({"teachers", "states", "reviews"})
#OnDelete(action = OnDeleteAction.CASCADE)
#OneToOne
#JoinColumn(name = "course_code", referencedColumnName="code")
private Course course;

Foreign keys guarantee that an entry will exist in another table. This is a way of ensuring data integrity. SQL will never allow you to delete this entry while it still deletes in the other table. Either (1) this is letting you know you would have made a grave mistake by deleting this thing which is required or (2) you would like to put in a cascading delete so that not only is this entry deleted but so is what is supposed to be referencing it in the other table. Information on cascading deletes can be found here and written fairly easily (https://www.techonthenet.com/sql_server/foreign_keys/foreign_delete.php). If neither of these two descriptions fits you, evaluate why your foreign key relationship exists in the first place because it probably should not.

Try this method too. I got the answer with this method,This is just a test to remove.
Pay attention to the cascade!
MyUser Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String firstname;
private String lastname;
private String mobile;
#Column(unique = true)
private String email;
private Long date;
private LocalTime localiime;
private LocalTime localiimeend;
#ManyToOne(fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "role_fk"))
private Role role;
Role Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
#OneToMany(mappedBy = "role", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<MyUser> users;
#ManyToOne (fetch = FetchType.LAZY,cascade = CascadeType.MERGE)
#JoinColumn(foreignKey = #ForeignKey(name = "rolecat_fk"))
private rolecat rolecat;
rolecat Entity
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
#OneToMany(mappedBy = "rolecat", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<Role> roles;

Related

Spring Data Jpa OneToMany save bidirectional

I have a problem with saving child entities.
Here is my example. My model classes look like this:
#Entity
public class ImportDocument {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private boolean imported;
#Transient
private Status status;
#Basic
private char statusValue;
#OneToMany(mappedBy = "importDocument" , cascade = {CascadeType.ALL})
private List<ImportDocumentItem> importDocumentItems;
}
#Entity
public class ImportDocumentItem {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumn(name = "import_document_id")
#JsonIgnore
private ImportDocument importDocument;
}
I have implemented JpaRepository interfaces for both domain classes.
I try to save with:
importDocumentRepository.save(importDocument);
When I save ImportDocument object, everything is inserted. But the problem is that, the import_document_item.import_document_id (which is foreign key of import_document_id) attribute is filled with null value, not with id of import_document that I expected. How can I fix this issue?
Thanks a lot.
You have to set entity relations on both side before saving. Here an example
ImportDocument importDocument = new ImportDocument();
//...
importDocument.setImportDocumentItems(items);
items.forEach(ImportDocumentItem::setImportDocument);
importDocumentRepository.save(importDocument);

Why my foreign key is null when saving to h2 db, using JPA

What is the difference between these 2 codes. The 1st one shows null on my foreign key which is individualId. The 2nd one is not. Why?
//1st code:
#Entity
#JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Individual {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "INDIVIDUAL_ID")
private Long individualId;
#OneToMany(mappedBy="individual",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Identification> identifications = new ArrayList<Identification>();
}
#Entity
public class Identification {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "IDT_ID")
private Long id;
#ManyToOne
#JoinColumn(name="individualId")
private Individual individual;
//second code
//replaced #OneToMany in the first code & then i just dont add #ManyToOne in the Identification Class and it works fine. Why?
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "INDIVIDUAL_ID", referencedColumnName = "INDIVIDUAL_ID")
private List<Identification> identifications = new ArrayList<Identification>();
When i search for JPA tutorial in google the 1st code is the one that i always read. declare #OneToMany in the parent class and add mappedBy, declare #ManyToOne in the child class. But why the 2nd code works perfect than the 1st code? it just let me declare #OneToMany only in the parent class ?
In the class Identification the name of the #JoinColumn does not match any column in your class Individual. It must be the name of the column in the database, which is INDIVIDUAL_ID:
#JoinColumn(name="INDIVIDUAL_ID")

How to show 2 different type of mappings between 2 entity classes, holding each other's reference

The mappings between the 2 tables(Department and Employee) is as follows (Link for the image showing mapping is also provided):
Every department has one and only one department head.
Every department can have more than one employee.
dept_id and empId are primary keys of their respective tables.
dept_head(It is the Employee Id) and dept are foreign keys of their
respective tables.
Mapping Employee and Department table
I created entity classes for the above 2 tables (The structure is provided below).
Employee Class:
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "empId")
private Integer empId;
#Size(max = 45)
#Column(name = "name")
private String name;
#Size(max = 45)
#Column(name = "address")
private String address;
#Size(max = 45)
#Column(name = "grade")
private String grade;
#Size(max = 45)
#Column(name = "email")
private String email;
#JoinColumn(name = "dept", referencedColumnName = "dept_id")
#ManyToOne
private Department deptartment;
.. ...
}
Department class:
public class Department implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Basic(optional = false)
#NotNull
#Size(min = 1, max = 8)
#Column(name = "dept_id")
private String deptId;
#Size(max = 45)
#Column(name = "name")
private String name;
#JoinColumn(name = "dept_head", referencedColumnName = "empId")
#OneToOne
private Employee deptHead;
#OneToMany(mappedBy = "deptartment")
private List<Employee> employeeList;
....
...
}
If I am adding mappedBy in Employee Class (like I did in Department), to show OneToOne mapping between empId and deptHead,the code is compiling and running. However, If I do not add the mappedBy statement in Employee class, as the above code shows, the code still compiles and runs fine.
I would want to know why the code above works even if I am not providing mappedBy in employee class.
If anybody can help me clearing the above doubts and explaining the logic behind its working would be great as I am new to this.
It is not quite clear where you tried to user it with and without the mappedBy attribute.
But if I get your question correctly, you ask why you can have only one or both sides annotated?
It depends on which side is the source and destination of your relation or wheter it's bi-directional. On the Java-side you can have a relation always in both directions due to object references, but on the Database-side, you might only have it in one direction.
Check out JPA Wiki book on that topic for more details.
Additionally, the API doc for OneToOne states:
Specifies a single-valued association to another entity that has
one-to-one multiplicity. It is not normally necessary to specify the
associated target entity explicitly since it can usually be inferred
from the type of the object being referenced. If the relationship is
bidirectional, the non-owning side must use the mappedBy element of
the OneToOne annotation to specify the relationship field or property
of the owning side.

JPA Many to One relationship

I am bit beginner on JPA and need some help on fetching the Many to One relationship in JPA.
I have below entities.
User which stores all user information . User extends Audiatable abstract class which is for holding auidt paramters like last modified date, creation date etc.
I am trying to add another fields as lastUpdatedByUser which should get fetched from lastUpdatedBy for which I amtrying to add Many-One relationship.
But the relation is not working somehow, am I doing something wrong here?
AuditableEntity.java
public abstract class AuditableEntity<T extends Entity<T, ID>, ID> implements Auditable {
private static final long serialVersionUID = 1L;
#Column(name = "cruserid")
private Long createdBy;
#Column(name = "crdate")
#Type(type = JpaConstants.TYPE_LOCAL_DATE_TIME)
private LocalDateTime createdOn;
#Column(name = "chuserid")
private Long lastUpdatedBy;
#Column(name = "chdate")
#Type(type = JpaConstants.TYPE_LOCAL_DATE_TIME)
private LocalDateTime lastUpdatedOn;
#Transient
#ManyToOne(fetch = FetchType.LAZY, targetEntity = User.class)
#JoinColumn(name = "usrId", referencedColumnName = "chuserid")
private User lastUpdatedByUser;
User.java
public class User extends AuditableEntity<User, Long> {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "usrId")
private Long id;
#Column(name = "usrName")
private String name;
#Column(name = "loginame")
private String loginName;
}
Well, you marked the association with #Transient, which means that the field is not persistent and should be ignored by JPA.
And you also seem to have two different fields to store the same information: lastUpdatedBy and lastUpdateByUser. Remove the first one, and map the second one as
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "chuserid")
private User lastUpdatedByUser;
This tells that the association is a ManyToOne to the User entity (no need to specify the targetEntity since it's the type of the field), and that this association is materialized by the join column named "chuserid", in the auditable entity's table, and referencing the ID of the User entity (referencedColumnName is only useful when you use composite IDs, or when you reference an entity by a column which is the the ID)

merging entity with onetomany mapping and #version field causes delete of the previous mapping

Hi! All,
I have a mapping issue with two entities. mapped through a #OneToMany unidirectional relation. I have an entity Artifact which can have multiple Revision. Here's how I have mapped them
#Entity
#Table(name = "artifact")
public class Artifact implements Serializable {
private static final long serialVersionUID = 248298400283358441L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Version
private Integer version;
...
#OneToMany(cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH, CascadeType.REMOVE })
#JoinTable(name = "artifact_revisions", joinColumns = #JoinColumn(name = "artifact_id"), inverseJoinColumns = #JoinColumn(name = "revision_id"))
private Set<Revision> revisions;
And the revisions entity
#Entity
#Table(name = "revision")
public class Revision implements Serializable {
private static final long serialVersionUID = -1823230375873326645L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
...
#Column(name = "date_created", nullable = false)
#Temporal(TemporalType.TIMESTAMP)
private Date creationDate;
The revision table saves the filed name that was updated; old value and new value etc.
The problem I face is that when I update the artifact; the last mapping gets deleted and then it inserts a new one, so if effect I only have the last but one revision available not the entire revision history.
Hibernate:
update
artifact
set
description=?,
estimate=?,
name=?,
rank=?,
status=?,
sysId=?,
version=?
where
id=?
and version=?
Hibernate:
delete
from
artifact_revisions
where
artifact_id=?
and revision_id=?
Hibernate:
insert
into
artifact_revisions
(artifact_id, revision_id)
values
(?, ?)
If I remove #version annotation from the artifact it works fine.
Is it because I am mapping the relation in a wrong manner? Should this relation be mapped as an element collection instead?
There is another Entity Task which is to be mapped with the Revision entity. So what will be the best approach here?
Maybe this is not a straight answer to your question but I think you should look into hibernate envers. I think it's doing pretty similar thing. (envers stands for entity versioning). You just annotate entity with #Audited put some listeners into config and the rest magic is done for you.