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.
Related
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.
I have a simple unidirectional ManyToOne relationship on an entity, which unfortunately is defined in a schema I cannot change.
It is defined as follows
#Entity
#Table(name="Profile")
...
public class Profile{
#ManyToOne
#JoinColumn(name="usr_id", nullable=false, updatable=false)
private User usr;
...
and all is well. The relationship is enforced with a foreign key in the db, hence the nullable = false and updatable = false. There is no mention of the profiles in user.
When I try to delete a Profile, hibernate also tries to delete the User entity, which is parent to other relationships and therefore fails. I have no CascadeType annotations anywhere.
My intent is to have a simple reference to the user using this profile in the usr field. This is a unidirectional relationship. The user entity should not be affected whenever I delete a profile.
This appers to be achievable when the usr field may be dereferenced before delete (I can see in the hibernate generated sql that hibernate attempts to set the field to null before deletion) - however that fails because of the foreign key.
Is what I'm trying to do achievable? If so, how?
(I'm using spring data on top of hibernate, if that is relevant.)
further Infos: I have tried optional=false, and it leads to the delete the parent entity behaviour. I have tried all fitting combinations of CascadeTypes, #OnDelete with NO_ACTION (still tries to delete the user) and defining a reverse but owned by user relationship - no success so far. On top of that, I tried the search function ;), which lead me to the conclusion that this is just my problem. If I missed an answered question, I'd appreciate a pointer in the right direction. Thanks.
Do you have some kind of other non-nullable association #ManyToOne or #OneToOne to the Profile entity? You can debug into the deletion process by setting a break point in e.g. the JDBC driver in e.g. Connection.prepareStatement and go down the stack frames to the cascading part to figure out why this cascading happens.
If all that doesn't help, please create a reproducing test case and submit an issue to the Hibernate issue tracker.
I am yet again stuck with trying to delete data with Hibernate..
I am at point where I am starting to just stack annotation, hoping something would work... so far nothing has.
I need to delete old calculation when starting new for same time period.
I have the following query
#Modifying
#QueryHints(value = #QueryHint(name = HINT_FETCH_SIZE, value = "10"))
#Query(value = "DELETE FROM Group a WHERE a.c_date BETWEEN :dateA AND :dateB")
void deleteOld(#Param("dateA") LocalDate dateA, #Param("dateB") LocalDate dateB);
which uses entity Group, which has (on top of normal String, LocalDate and long types) attribute
#OneToMany(cascade=CascadeType.ALL, mappedBy = "owner", orphanRemoval = true)
#JsonManagedReference
#OnDelete(action = OnDeleteAction.CASCADE)
private List<Instrument> instruments = new ArrayList<>();
But I still get violated - child record found every time I try to run delete method.
I keep finding more and more annotations like this, from threads where people have the same kind of problems, but I would love to understand why is this a problem. From what I read Cascade and orphanRemoval should be all I need, but it sure does not seem to be.
Hibernate: 5.2.17.Final
Please help me to understand, why is this happening ?
The #OnDelete will delete records using a ON DELETE rule on the database, when using Hibernate to generate the schema. If you manage your own schema this will have no effect.
The #QueryHints you have specified doesn't really make sense here, for an DELETE query that is. Nothing will be fetched.
The fact that you are using an #Query basically bypasses the configuration in the #OneToMany, simply due to the fact that you write a query and apparently know what you are doing. So the mapping isn't taken into account.
If you want to delete the childs as then you have 3 options:
Add an additional query and first remove the childs, then the parents
Add an ON DELETE rule to your database, to automatically remove the childs
Retrieve the Group and remove using EntityManager.remove which will take the #OneToMany mappings into account as now Hibernate needs to manage the dependencies between the entities.
I have 2 classes in java with relation parent-child and I have a problem with the delete child. when I delete a child the function return true but when I check database nothing happens, the child still not deleted.
this is my class parent : Engagement.java
#OneToMany(fetch=FetchType.EAGER, mappedBy="parent", cascade=CascadeType.ALL)
private Collection<Sub_Engagement> subs_engs;
this is my class child: Sub_Engagement.java
#ManyToOne
#JoinColumn(name="parent")
private Engagement parent;
with this code POST/GET/PATCH work fine but DELETE not working.
I tried a solution like that:
#OneToMany(fetch=FetchType.EAGER, mappedBy="parent", orphanRemoval = true, cascade = { CascadeType.MERGE, CascadeType.REFRESH, CascadeType.DETACH, CascadeType.REMOVE})
private Collection<Sub_Engagement> subs_engs;
and DELETE works but PATCH/PUT not working when I try update a child.
Thanks in advance :)
Andronicus had it right, that really should work. It's weird that you have an either-or situation with you updates and deletes.
Double check your persistence.xml. Is there something that overrides the annotations?
I also recommend to enable tracing and check the sql queries actually executed. Is the delete statement logged? Is your transaction actually committed?
Can we look at the code you are using for removing the entities? Are you using entitymanager's remove() method or executing some custom jpql?
Also, are you using a CMT (for example EJBs) or are you handling the transactions yourself via JTA?
I have a problem with Hibernate (Thanks to Thomas now the problem is more legible).
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:
User Profile
M______N
|
attribute
There are 3 tables here:
"User", "Profile" and "User_Profile".
Now imagine User_Profile has 1 attribute for the relation (and obviously the keys).
Ok, now this is translating to Hibernate by the following:
User:
// The relation is Many to Many, but considering that it has an attribute, this is OneToMany with the ManyMany RelationShip
#OneToMany(mappedBy="user", targetEntity=UserProfile.class)
#Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
#LazyCollection(LazyCollectionOption.TRUE)
private Set<UserProfile> userProfile = new HashSet<UserProfile>();
UserProfile:
#Id
#ManyToOne(targetEntity=User.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private User user;
#Id
#ManyToOne(targetEntity=Profile.class,fetch=FetchType.LAZY)
#Cascade({CascadeType.LOCK})
#JoinColumns({ #JoinColumn(name="...", referencedColumnName="...") })
private Profile profile;
So, I think the configuration is correct, and the save, independently if the User has Profile childrens save all of them. The problem is when I try to update the user:
getHibernateTemplate().getSessionFactory().getCurrentSession().clear();
getHibernateTemplate().saveOrUpdate( user );
getHibernateTemplate().getSessionFactory().getCurrentSession().flush();
Hibernate don´t delete the Profile relation if there is an empty set of Profile childrens. Only add the profiles (override the old)... That´s rare... What´s the problem?
Thank you in advance
All you actually do is remove the relation and thus theres no DELETE to cascade, that's why nothing gets deleted.
Try adding the Hibernate cascade type DELETE_ORPHAN (using the #Cascade annotation) to make Hibernate delete entities that are not referenced anymore.
Additionally, I'd not remove the Mini entities alone. If there's no relation, i.e. the set of Minis is empty, it normally makes no sense to keep the SuperMini entities that now represent an empty collection (in rare cases it might make sense, just want you to think about whether you need them or not).
Edit:
Note that with DELETE_ORPHAN you should reuse the set, otherwise all the relations might be deleted and reinserted.
Basically Hibernate would then see the set being changed and would issue a delete for the "old" set and a reinsert for the "new" set. This could be wanted but in case you only want an update, i.e. only delete the entities that are not in the set anymore, you should do the following:
clear the set
add the "new" set to the reused and now cleared set using addAll(...)
This should trigger the update (and deletion of orphans) only.