Spring + Hibernate :Unable to delete child in unidirectional relationship - java

i tried finding similar issue here and i found this here
but it did not help much.
this here is the exact issue i have. Thing is the #OnDelete forces me to make a bidirectional relationship right? I would like to keep it unidirectional if possible.
What i want to do is delete a comment (child) from a post (parent);
every time i am trying to do that i receive this error
java.sql.SQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (`blogapp2`.`post_comments`, CONSTRAINT `FKrvgf8o4dg5kamt01me5gjqodf` FOREIGN KEY (`comments_id`) REFERENCES `comment` (`id`))
this is my code
the delete method
#GetMapping("/dashboard/showposts/deletecomment/{id}")
public String deleteComment(#PathVariable("id") Long id) {
commentService.deleteComment(id);
return "redirect:/admin/dashboard/showposts";
}
in Post
#OneToMany(cascade = CascadeType.ALL)
private List<Comment> comments = new ArrayList<>();
in Comment there is no reference to the Parent.
How can i delete the Comment from the database? i mention that cascade works with no issues so when i delete the parent all the children are deleted aswell.
Any help would be greatly appreciated !
L.E:I modified my handler method to look like this
#GetMapping("/dashboard/showposts/deletecomment/{id}")
public String deleteComment(#PathVariable("id") Long id) {
List<Comment> comments = commentService.findAll();
Comment comment = commentService.findBYId(id);
comments.remove(comment);
return "redirect:/admin/dashboard/showposts";
}

You can do it without creating bidirectional relationship but there is one caveat in doing so and I would come to that later, let's see how you can do that in unidirectional relationship.
First you need to specify the orphanRemoval=true on your entity relation
#OneToMany(cascade = { CascadeType.ALL }, orphanRemoval = true)
List<Comment> comments = new ArrayList();
// Now I am assuming you have equals and hash code methods are implemented in your comment class,
// So all you need to load the Comment Entity by its id and then have to call
Comment comment = dao.findById(id);
comments.remove(comment);
// This will delete the comment from table and will keep all comments as it is.
// Another way is to iterate the comments list and find matching Comment object (add the method in transaction)
#Service
class PostService {
#Transactional
public Comment deleteComment(Integer commentId) {
Post post = repository.findById(id);
List<Comment> comments = post.getComments();
Comment comment = comments.stream().filter(c -> c.getId().equals(commentId)).findAny()
.orElseThrow(() -> new IllegalArgumentException("Invalid comment id"));
comments.remove(comment);
return comment;
}
}
Caveats:
1. post.getComments() // will load all comments from database
2. comments.remove(comment) // will trigger additional INSERT statements
Why additional insert statements will be triggered?
When you use uni-directional mapping, JPA provider (hibernate) will create additional junction table and your relation becomes #ManyToMany at the background.
So it will first Delete all the entries from junction table passing associated post_id and then insert all the records back to junction table, leaving the record we deleted.
Then it will delete the entry from comment table, so its a performance penalty you have to pay using unidirectional relation.

By the error message, I assume that besides tables post and comments, you have a join table named post_comments.
If thats the case, hibernate is not aware of it. You should use not only #OneToMany but #JoinTable annotation as well in the commentsfield of Post class.

Related

Updating a "complex" Entity in JPA

entity = MainDao.findByUUID(getEntityManager(), dto.getUuid());
Adress i = new AdressDao().findById(dto.getIdAdress());
i.setPhone(dto.getPhone());
entity.setAdress(i);
return MainDao.update(getEntityManager(), entity);
I have a main Entity in which there is a #ManytoOne relationship to Adress. I want to update the field "phone" inside adress, how do I do it? My code fails to do so.
Hope you can help me out, it seems there is no "patch" method inside JPA. I would love to know the best practices.
By default #ManyToOne doesn't cascade the changes (as it refers to a parent which may be have other child associations).
You can do either of below,
save the changes of Address entity via your AddressDao like addressDao.save(addressEntity)
use #ManyToOne(cascade = CascadeType.ALL).
1st options is preferable.
Read about CascadeType to utilize wisefully.

Should Hibernate/JPA OneToMany be updated on both sides?

I have defined entities relation in Hibernate ORM with following code:
#Entity
public class Treatment {
#OneToMany(fetch = FetchType.EAGER, mappedBy="treatment")
private List<Consultation> consultations;
...
}
#Entity
public class Consultation {
#ManyToOne(fetch=FetchType.EAGER)
#JoinColumn(name = "treatment_id")
private Treatment treatment;
...
}
My question is, how should I update Treatment/Consultation when I want to make relation?
Is it enough to update it on one side:
treatment.getConsultations().add(newCon);
Or should I update it on both sides?
treatment.getConsultations().add(newCon);
newCon.setTreatment(treatment);
How it looks in case of delete?
Well, using the mappedBy you tell Hibernate that the relationship is maintained by the other side, a filed called treatment in Consultation class. So first you have to get the consultation instance, then set the treatment, and finally persist the consultation instance. It will update all the references in the DB as integrity constraints (Primary Key/ Foreign Key pairs). So here the consultation table will have a treatmentId foreign key column pointing to the ID column (primary key) of the Treatment table.
an example code is,
Consultation consultation = new Consultation();
// This maintains the relationship.
consultation.setTreatment(treatment);
someDaoRepository.save(consultation);
Hope this helps, Happy Coding !

How to ignore unique violation during insert list of objects which contain set of object

I use PostgreSQL nad Spring data JPA with Hibernate. I have relation OneToMany with orphanRemoval = false because I very often add many childs to relation.
Parent:
#OneToMany(mappedBy = "parent", cascade = { CascadeType.ALL }, orphanRemoval = false, fetch = FetchType.LAZY)
public Set getChildren() {
return children;
}
Child:
#ManyToOne
#JoinColumn(name = "parent_id")
public Parent getParent() {
return parent;
}
To persist or merge object I use method
Iterable< T > save(Iterable< extends T> entities)
form CrudRepository. I save list of parents, where every parent contain set of child. Child table has unique constraint. If constraint violations occurs I want to ignore that and ommit (do not persist) child which cases viloations but I want insert every child which doesn't case constraint violation. How to do that?
Handle this dirty by Exceptions.
Try to Update the Database, if fine break here.
Catch the UniqueViolationException and find the JDBCException. Upcast to your qualified Database Exception and find the broken Children.
Remove the Children from the Parent.
Go to 1.
The clean way is to filter the Entitys who will produce unique-violation-exceptions.
After you filtered that entitys, you can save the good ones.
Exceptions should used as they realy are: Exceptions.
This is a Postgres-specific answer and not very kosher, but you could employ a similar approach with different DBs:
take the advantage of on conflict clause in native Postgres insert statement. That way you won't have to handle the unique constraint exceptions at all, the DB will solve the conflicts for you as it encounters them.
Prepare a native SQL insert statement e.g.
insert into child (id, parent_id, data) values (:id, :parent_id, :data) on conflict do nothing
Use the previously written statement with javax.persistence.EntityManager#createNativeQuery(java.lang.String)
EntityManager.createNativeQuery(sql)
.setParameter("id", it.id)
.setParameter("parent_id", parentId)
.setParameter("data", it.data)
.executeUpdate()
Repeat for all the children

Hibernate: One To Many Relation on cascade save but doesn't delete

I have a problem with Hibernate.
In Short:
How to configure a ManyToMany association with Hibernate when the relationship has an attribute and we need save, delete and update in cascade?
In Large:
Imagine the following DataBase:
Super Mini
M______N
|
attribute
There are 3 tables here:
"Mini", "Super" and "Super_Mini".
Now imagine Super_Mini has 1 attribute for the relation (and obviously the keys).
Ok, now this is translating to Hibernate by the following:
Super:
// The relation is Many to Many, but considering that it has an attribute, this is OneToMany with the ManyMany RelationShip
#OneToMany(mappedBy="mini", targetEntity=Mini.class)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#LazyCollection(LazyCollectionOption.TRUE)
private Set<SuperMini> superMini = new HashSet<SuperMini>();
SuperMini:
#Id
#ManyToOne(targetEntity=Super.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private Super super;
#Id
#ManyToOne(targetEntity=Mini.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private Mini mini;
So, I think the configuration is correct, and the save, independently if the object has Mini childrens save all of them. The problem is when I try to delete the object:
Super data = getHibernateTemplate().load(Super.class, idSuper);
getHibernateTemplate().getSessionFactory().getCurrentSession().clear();
data.setMini( new HashSet<Mini>() );
getHibernateTemplate().delete( data );
getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
Hibernate don´t delete the Mini relation... What´s the problem? I know how to solve it by HQL, but maybe the configuration is not correct, I don´t know.
Thank you in advance,
Your question is not clear. Super does not contain a Set<Mini2>. It contains a Set<SuperMini2>. So the last code snippet doesn't make much sense.
Moreover, the targetEntity attribute on Super.superMini2 is incorrect, and unnecessary.
CascadeType.ALL include CascadeType.DELETE, so it's also unnecessary.
But to answer your question, I think the problem is that deleting Super cascades to SuperMini2 because the association has a cascade delete, but there is no cascade delete between SuperMini2 and Mini2, so of course, Mini2 instances are not deleted.
EDIT:
The answer is that the OP, before editing the question, removed all the entities from the collection of SuperMini before deleting the Super entity. So the cascade delete on the collection of Supermini didn't have anything to delete anymore.

Lazy loading problem with one to one mapping

I have 2 very simple POJOs and i have just one to one mapping between them.
contact.java
comment.java -- it has foreign key column to the contact.java
the code i have written is below.
contact.java
#OneToOne(optional= false,cascade = CascadeType.ALL, mappedBy="contact", fetch=FetchType.LAZY)
#org.hibernate.annotations.LazyToOne(org.hibernate.annotations.LazyToOneOption.NO_PROXY)
private Comment mComment;
comment.java
#OneToOne(fetch = FetchType.LAZY,cascade=CascadeType.ALL )
#JoinColumn(name="EW_CNTC_ID")
private Contact contact;
i am setting comment into contact pojo and finally i am saving contact.java
if i keep #OneToOne(optional= false, i am getting dataintegrityexception, constraintvoilationexception
if i changed #OneToOne(optional= true, then it is working.
i think that if optional is false, it is trying to insert contact, it find comment it is trying to insert comment, but it has reference to contact it has to set the foreign key without inserting contact it cannot keep foreign key ..
if optional true the contact can be inserted without comment and PK generated for contact and tat is set in the foreign key column of the contact.--- anyway this issue is solved.
one more thing i am loading contacts i need to lazy load the comments, it is no where working can some one help on this, i strictly need lazy loading of comment because of performance problm.
Thanks in advance.
We have this issue in hibernate 3. Hibernate 4 fixed this issue.
If you want to load only contacts not comments then try like this(Imply change it to manytoone relation).
#ManyToOne(fetch=FetchType.LAZY)
private Comment mComment;

Categories