Delete object from ManyToOne relationship doesn't update a parent collection - jpa

I have ManyToOne relationship between Parent and Child. I want to Delete multiple Child entities that answer a certain query.
The problem is that after I run a Delete query, Parent.getChildren() still returns the deleted children.
Can't I use Delete queries in such case?
#Entity
#Table(name = "CHILD_DATA")
public class Child {
private Parent parent;
}
#Entity
#Table(name = "PARENT")
public class Parent{
private Set<Child> children;
#Column(name = "CHILDREN")
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
public Set<Child> getChildren() {
return children;
}
}
public class ChildDAO{
public int removeServiceFrontPageData(Parent parent, long serviceID){
String query = "DELETE FROM Child WHERE parent =:parent";
Query q = em.createQuery(query);
q.setParameter("parent", parent);
return q.executeUpdate();
}
}
To refresh a parent entity, I use the following function:
public class ParentDAO{
public Parent getParent(String parentID){
final String select = "FROM Parent WHERE parentID = :parentID";
Query q = em.createQuery(select);
q.setParameter("parentID", parentID);
if(q.getResultList().isEmpty()){
return null;
}
return (Parent) q.getSingleResult();
}
}
thanks

My solution is based on another post
I found 2 ways to solve it:
1) update a parent if I remove children:
select child entities that should be removed.
remove these entities from parent.getChildren()
delete the children from database
2) add orphanRemoval=true flag on parent.getChildren(). Remove children from set will remove them from database
#Entity
#Table(name = "PARENT")
public class Parent{
private Set<Child> children;
#Column(name = "CHILDREN")
#OneToMany(mappedBy = "parent", cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
public Set<Child> getChildren() {
return children;
}

Related

Cannot delete or update a parent row: a foreign key constraint fails Spring JPA

I have this query
DELETE
FROM bookings as b
WHERE b.check_out = CURRENT_DATE;
and I get
Cannot delete or update a parent row: a foreign key constraint fails (online_booking_app.booked_rooms, CONSTRAINT FK3x1lpikb2vk75nx41lxhdicvn FOREIGN KEY (booking_id) REFERENCES bookings (id))
My Booking entity has CascadeType.ALL and mapped by matches the other side - from my research these are some of the mistakes that could lead to this message.
Here is the BookingEntity:
#Entity
#Table(name = "bookings")
public class BookingEntity extends BaseEntity {
#OneToMany(mappedBy = "booking",cascade = CascadeType.ALL, orphanRemoval = true)
private List<BookedRoomsEntity> bookedRooms = new ArrayList<>();
private String firstName;
private String lastName;
public List<BookedRoomsEntity> getBookedRooms() {
return bookedRooms;
}
public BookingEntity setBookedRooms(List<BookedRoomsEntity> bookedRooms) {
this.bookedRooms = bookedRooms;
return this;
}
BookedRoomsEntity
#Entity
#Table(name = "booked_rooms")
public class BookedRoomsEntity extends BaseEntity {
#ManyToOne()
private BookingEntity booking;
public BookingEntity getBooking() {
return booking;
}
public BookedRoomsEntity setBooking(BookingEntity booking) {
this.booking = booking;
return this;
}
The CascadeType does only apply to EntityManager operations.
You therefore have two options:
Load the entities to be deleted first and then use EntityManager.remove
Remove the referencing entities first with a separate JPQL statement.

JPA Mapping OneToMany with partial embedded id child

Simple example (hopefully). I have a primary key (using a sequence) in one table and that value is a partial FK into a child table. I see the Parent is trying to be saved with a generated sequence, but then I see an exception that the parentId in the embeddable is null while saving the child. The sequence value used for the parent is not being carried over to the child. I have tried many annotations and mappedBy/join column names but no luck.
Any pointers would be very much appreciated.
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "audit_seq")
#SequenceGenerator(name = "audit_seq", allocationSize = 5)
private Long id;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent")
private List<Child> childList = new ArrayList<>();
//Used to add child record o the parent
public void addChild(Child child) {
this.childList .add(child);
child.setParent(this);
}
}
#Embeddable
public class ChildId {
private Long parentId;
private String name;
}
public class Child {
#EmbeddedId
private ChildId id;
private String myCol;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name = "parentId", insertable = false, updatable = false)
private Parent parent;
}
I was able to get this resolved with the use of a couple of annotations:
Parent class:
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "parent", orphanRemoval = true)
#PrimaryKeyJoinColumn
private List<Child> childList = new ArrayList<>();
Child class:
#ManyToOne
#MapsId("id")
#JoinColumn(name = "id")
private Parent parent;
Now all objects are being persisted when saving the parent with the appropiate sequence id.

Commiting a JPA transaction after remove

I am having a specific scenario, where 2 tables are having parent child relationship. When i m invoking the api to delete a child, it works fine. And then when I try to invoke the api to delete the parent, I get optimistic lock exception.
Upon investigation, I realized that while deleting the parent, it is trying to delete the child as well which has already been deleted.
Below is my code snippet. Any suggestion or help would be great.
This is the code for deleting the child.
#Transactional(Transactional.TxType.MANDATORY)
public void deleteChild(int id) {
var child= entityManager.find(Child.class, id);
entityManager.remove(child);
}
And this is the code for deleting the parent.
#Transactional(Transactional.TxType.MANDATORY)
public void deleteParent(int id) {
var parent = entityManager.find(Parent.class, id);
entityManager.remove(parent);
}
The Parent entity has OneToMany relationship with the Child entity.
#Table(name = "PARENT")
#Access(value = AccessType.FIELD)
public class Parent{
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "PARENT_ID")
private List<Child> child;
}

spring data JPA deletion

I am getting a foreign key violation when I try to delete a record.
I have this record:
#Entity
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long parentId;
#OneToOne(cascade = CascadeType.ALL, orphanRemoval = true)
#JoinColumn(name = "childId")
private Child child;
and
#Entity
#Table(name = "Child")
public class Child {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long childId;
public Long getOperatoryId() {
return id;
}
When I try to delete the child, I get a key violation because there are some parent records that point to the children. I thought I could delete the parent first, then go delete the children as:
parentRepository.delete(parent)
but I get an error that the property id doesn't exist on child. Is this because the child id is named childId and not id?
Here I worked on similar example what did you asked for. Customize it based on your need.
SQL Server table create query
CREATE TABLE "DBO"."Parent"(
Parent_Id INT PRIMARY KEY NOT NULL,
Parent_Name VARCHAR(255) NOT NULL)
CREATE TABLE "DBO"."Child"(
Child_Id INT PRIMARY KEY NOT NULL,
Child_Name VARCHAR(255) NOT NULL,
Parent_Ref_Id int not null,
CONSTRAINT FK_Parent_Ref_Id FOREIGN KEY (Parent_Ref_Id) REFERENCES Parent(Parent_Id)
)
Spring data JPA code
Parent Entity
#Entity(name = "Parent")
#Table(name = "Parent")
public class Parent {
#Id
#Column(name = "Parent_Id")
private long parentId;
#Column(name = "Parent_Name")
private String parentName;
//cascade = {CascadeType.REMOVE} OR orphanRemoval = true
#OneToOne(cascade = {CascadeType.ALL},orphanRemoval = true,fetch = FetchType.LAZY,mappedBy="parentInfo")
private Child childInfo;
public long getParentId() {
return parentId;
}
public void setParentId(long parentId) {
this.parentId = parentId;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public Child getChildInfo() {
return childInfo;
}
public void setChildInfo(Child childInfo) {
this.childInfo = childInfo;
}
}
Child Entity
#Entity(name = "Child")
#Table(name = "Child")
public class Child {
#Id
#Column(name = "Child_Id")
private int childId;
#Column(name = "Child_Name")
private String childName;
#OneToOne
#JoinColumn(name = "Parent_Ref_Id", referencedColumnName = "Parent_Id")
private Parent parentInfo;
public int getChildId() {
return childId;
}
public void setChildId(int childId) {
this.childId = childId;
}
public String getChildName() {
return childName;
}
public void setChildName(String childName) {
this.childName = childName;
}
public Parent getParentInfo() {
return parentInfo;
}
public void setParentInfo(Parent parentInfo) {
this.parentInfo = parentInfo;
}
}
Child Repo Code
public interface ChildRepo extends CrudRepository<Child,Long> {
}
Parent Repo Code
public interface ParentRepo extends CrudRepository<Parent,Long> {
Parent findByParentId(long id);
}
Controller Code
#Autowired
private final ParentRepo parentRepo;
....
private void save() {
//Save the parent
Parent p = new Parent();
p.setParentId(1);
p.setParentName("Parent1");
Child c = new Child();
c.setChildId(1);
c.setChildName("Child1");
c.setParentInfo(p);
p.setChildInfo(c);
parentRepo.save(p);
//delete the parent and child as well
Parent p1 = parentRepo.findByParentId(1);
parentRepo.delete(p1);
}

JPA Persist parent and child with one to many relationship

I want to persist parent entity with 20 child entities,
my code is below
Parent Class
#OneToMany(mappedBy = "parentId")
private Collection<Child> childCollection;
Child Class
#JoinColumn(name = "parent_id", referencedColumnName = "parent_id")
#ManyToOne(optional=false)
private Parent parent;
String jsonString = "json string containing parent properties and child collection"
ObjectMapper mapper = new ObjectMapper();
Parent parent = mapper.readValue(jsonString, Parent.class);
public void save(Parent parent) {
Collection<Child> childCollection = new ArrayList<>() ;
for(Child tha : parent.getChildCollection()) {
tha.setParent(parent);
childCollection.add(tha);
}
parent.setChildCollection(childCollection);
getEntityManager().persist(parent);
}
So if there are 20 child tables then I have to set parent reference in each of them for that I have to write 20 for loops?
Is it feasible? is there any other way or configuration where I can automatically persist parent and child?
Fix your Parent class:
#OneToMany(mappedBy = "parent")
mappedBy property should point to field on other side of relationship. As JavaDoc says:
The field that owns the relationship. Required unless the relationship is unidirectional.
Also you should explicitely persist Child entity in cycle:
for(Child tha : parent.getChildCollection()) {
...
getEntityManager().persist(tha);
...
}
As Alan Hay noticed in comment, you can use cascade facilities and let EntityManager automatically persist all your Child entities:
#OneToMany(mappedBy = "parent", cascade = CascadeType.PERSIST)
More details about cascades (and JPA itself) you can find in Vlad Mihalcea's blog.
Generally, #JoinColumn indicates that the entity is the owner of the relationship & mappedBy indicates that the entity is the inverse of the relationship.
So, if you are trying like following
#OneToMany(mappedBy = "parent")
private Collection<Child> childCollection;
That means it is inverse of the relationship and it will not set parent reference to its child.
To set parent reference to its child, you have to make the above entity owner of the relationship in the following way.
#OneToMany(cascade = CascadeType.ALL)
#JoinColumn
private Collection<Child> childCollection;
You need not set any child reference because above code will create a column in the child table.
As pointed out in the comments you must take care of the object graph consistency with child/parent relationship. This consistency won't come free when JSON is coming directly from i.e. a POST request.
You have to annotate the parent and child field with #JsonBackReference and #JsonManagedReference.
Parent class:
#OneToMany(mappedBy = "parentId")
#JsonBackReference
private Collection<Child> childCollection;
Child class:
#JoinColumn(name = "parent_id", referencedColumnName = "parent_id")
#ManyToOne(optional=false)
#JsonManagedReference
private Parent parent;
Similar question with answer is here
Furthermore, if you use #JsonBackReference/#JsonManagedReference on javax.persistence annotated classes in combination with Lombok's #ToString annotation you will incur in stackoverflow error.
Just exclude childCollection and parent field from the #ToString annotation with #ToString( exclude = ...)
The same will happen with Lombok's generated equals() method (#Data, #EqualsAndHashCode). Just implements those methods by hand or to use #Getter and #Setter annotations only.
I would let the parent persist it's own children
package com.greg;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
#Entity(name = "PARENT")
public class Parent {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "NAME")
private String name;
#Column(name = "DESCRIPTION")
private String description;
#OneToMany(cascade = CascadeType.ALL, fetch=FetchType.EAGER)
#JoinColumn(name = "parent", referencedColumnName = "id", nullable = false)
private List<Child> children = new ArrayList<Child>();
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<Child> getChildren() {
return children;
}
public void setChildren(List<Child> children) {
this.children = children;
}
}
I am using lombok to generate getter and setter properties on my entity classes.
I was also facing issue of NULL referenceID on child entity when I was trying to save parent Entity having child.
On my parent entity when I add children then I set "this" reference of parent on child.
In my example, I have User table and Address table where a User can have many addresses.
I have created domain classes as below.
e.g address.setUser(this);
package com.payment.dfr.entities;
import lombok.Data;
import javax.persistence.*;
import java.math.BigInteger;
#Entity
#Data
#Table(name="User")
public class User {
#Id
#GeneratedValue
private BigInteger RecordId;
private String Name;
private String Email;
#Getter(AccessLevel.NONE)
#Setter(AccessLevel.NONE)
#OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Address> addresses = new ArrayList<>();
public void addAddress(Address address){
address.setUser(this);
addresses.add(address);
}
}
#Entity
#Data
#Table(name="UserAddress")
public class Address {
#Id
#GeneratedValue
private BigInteger RecordId;
private String AddressLine;
private String City;
#ManyToOne(cascade = CascadeType.ALL)
#JoinColumn(name="UserId")
private User user;
}
This is how I save user with address
User newUser = new User();
newUser.setName("Papa");
newUser.setEmail("manish#gmail.com");
Address address1 = new Address();
address1.setAddressLine("4401 Central Ave");
address1.setCity("Fremont");
newUser.addAddress(address1);
Address address2 = new Address();
address2.setAddressLine("4402 Central Ave");
address2.setCity("Fremont");
newUser.addAddress(address2);
User user1 = userRepository.save(newUser);
log.info(user1.getRecordId().toString());