JPA CascadeType.ALL not deleting child - java

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?

Related

Hibernate Delete, child violation

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.

Java JPA loaded OneToMany list not updated after change

Maybe it is a really stupid question but I just can't find solution. I have:
#OneToMany(mappedBy = "project", fetch = FetchType.LAZY)
private List<Comment> comments;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "project_id")
private Project project;
When I launch application, I retrieve all comments by getter (it is loaded properly). When I however delete/add another comment, database is updated but application is not until rerun. When creating comment it is persisted - it is in DB - so I do not understand how is it not updated. Tried various cascade types but I do not think it is necessary here anyway. I also tried EAGER and it does not work as well.
When I manually get comments from DB after adding/deleting it works but that is I think very dirty solution right?
Thanks for any suggestions.
You have to fetch that entity you pasted a piece of code of once again. Already fetched list will not magicly update itself.
Its like using SQL - you have rerun the query to see the updates. ORM just maps SQL results row into objects.

Orphan deletion in Hibernate (when have multiple mapped objects)

I've got this structure of project:
class UserServiceSettingsImpl {
...
#ManyToOne
private UserImpl user;
#ManyToOne
private ServiceImpl service;
...
}
class ServiceImpl {
....
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, mappedBy = "service", orphanRemoval = true)
private Set<UserServiceSettingsImpl> userServiceSettings;
....
}
class UserImpl {
....
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "user", orphanRemoval = true)
private Set<UserServiceSettingsImpl> serviceSettings;
....
}
I am trying to delete Service and everything that belongs to it (UserServiceSettingsImpl), but accidentally, this settings are not being removed (I suppose because they are not orphans since UserImpl has them too). So the thing is: is there a way to delete Settings, without deleting them from user manually (there could be a lot of users with a lot of settings, iterating through it could take a lot of time) ?
You are correct in why the UserServiceSettings are not being deleted when deleting a service if they are also referenced by a User. They are not orphans and will have to be deleted explicitly per your business logic.
Three ideas:
Use the ORM to batch delete entities.
It's not much different than iterating, but might be optimized while still using the ORM.
List settingsCopy = new ArrayList<>(service.getSettings());
service.getSettings().clear();
myDao.deleteAll(settingsCopy);
Use direct HSQL/SQL to batch delete.
This depends on what framework you are using, but generally would be something like this, probably in your repository/dao class:delete from UserServiceSettingsImpl o where o.service.id = ? However, hibernate does not support JOINs when deleting, afaik, so this doesn't work as written. It's generally necessary to rework the HSQL to use a "delete where id IN(...)" type format.
Setup CASCADE DELETEs and CASCADE UPDATEs in your database DDL, outside of the ORM framework. (Not recommended.)
However, the last two options have problems if there is chance that service's and user's UserServiceSettings can be modified at same time via multiple threads (even with correct transaction boundaries), or if those entities will be used within the orm context after the delete without a reload. In that case, you will likely run in to unexpected and sporadic errors with the last two approaches, and instead, should iterate the settings and delete via the ORM, even if it is inefficient.
Even with the first approach, it can be tricky to avoid errors in highly concurrent environments when deleting shared entities.
You're correct that you cannot delete them in any kind of automatic way - they will never be orphans. I think the best you can do is just write yourself a helper method. e.g. if you have a ServiceDao class, you would just add a helper as:
public void deleteServiceAndSettings(Service service) {
for (UserServiceSettings setting : service.getUserServiceSettings()) {
session.delete(setting);
}
session.delete(service);
}

Multiple fetches with EAGER type in Hibernate with JPA

I have an entity which contains:
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "assessment")
#OrderBy(value = "order ASC")
private List<AssessmentPart> assessmentParts = new LinkedList<>();
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, mappedBy = "assessment")
private List<AssessmentText> texts = new LinkedList<>();
as you see there are two collections which needs to be eagerly loaded. This is not working and hibernate throws an exception:
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
That's because Hibernate can't fetch multiple collections with one go. But if I change the List to Set and the LinkedList to HashSet this part works fine but the other - more annoying issue occurs.
When I am trying to get the entity from the database using:
entityManager.find(entityClass, primaryKey);
It fails with:
org.hibernate.AssertionFailure: null identifier
I am sure that ID I am passing to find method is not null, I've debugged and I am sure of this. It somehow disappears inside Hibernate.
If I change collection types to LAZY everything just works without errors but there are some circumstances where I need to use EAGER.
Does anybody have a solution how to fix it? Either I could have a set but prevent assertion error from occurring or I could have a list but somehow avoid multiple fetch bags error.
I am using:
Hibernate 4.2.2.Final
Tomcat 7
JPA 2.0
JDK 1.7
EDIT
I've just discovered that adding #Fetch(FetchMode.SELECT) fixes the issue and I can use multiple list with EAGER type, but is there anyway to solve this by not using Hibernate specific annotations? And why it fixed the issue in the first place?
The root cause of the problem is that when Hibernate fetches SQL query results there is no simple way to tell which child element belongs to which collection. See this blog entry for more detailed explanation with an example. To summarize you have following workarounds:
Load each collection separately using subselect #Fetch(FetchMode.SELECT)
Force usage of list instead of bag by adding index column #IndexColumn(name="LIST_INDEX")
Use unordered collection like Set.
If you are using Hibernate and you do not care using Hibernate annotations :
Annotate your collection fields with:
#LazyCollection(LazyCollectionOption.FALSE)
Remember to remove the fetchType attribute from the #OneToMany annotation.

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.

Categories