I’m trying to use CASCADE for delete Page’s when a Issue obj is deleted. I also want to use orphanRemoval. Nothing is working. Page’s are never removed.
First of all, do not use CASCADE on JPA level (in @OneToMany annotation) - it is not needed in CUBA and can cause issues.
The @OnDelete(DeletePolicy.CASCADE) annotation should be enough to delete related Pages. How do you delete Issue? Can you provide a small project that reproduces the issue?
The problem is that if I don’t use CASCADE on JPA level the application does not save Page’s objects, because of:
org.springframework.dao.InvalidDataAccessApiUsageException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: net.gocoders.magazineappbackend.entity.Page-95e03344-1de8-5d78-8c3b-4f39ff19d3d5 [new].; nested exception is java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: net.gocoders.magazineappbackend.entity.Page-95e03344-1de8-5d78-8c3b-4f39ff19d3d5 [new].
Here is the Service Code:
import com.haulmont.cuba.core.Persistence;
import com.haulmont.cuba.core.Transaction;
import net.gocoders.magazineappbackend.entity.Issue;
import net.gocoders.magazineappbackend.entity.Page;
import org.springframework.stereotype.Service;
import javax.inject.Inject;
import java.util.List;
/**
* @author arimolo
*/
@Service(IssueService.NAME)
public class IssueServiceBean implements IssueService {
@Inject
private Persistence persistence;
@Override
public void updatePages(Issue issue, List<Page> pages) {
try (Transaction tx = persistence.createTransaction()) {
// Search for existing customers with the given name
issue = persistence.getEntityManager().find(Issue.class, issue.getId());
issue.setPages(pages);
tx.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
}
I also want to use orphanRemoval feature.
Do you see?
So what is the problem now? Pages are not deleted when you delete Issue? But your Issue entity is SoftDelete, so it is not deleted from the JPA point of view, hence it does not propagate deletion to Pages.
No, the problem is that pages are not save when issue is saved with a new list of pages.
“new object was found through a relationship that was not marked cascade PERSIST”.
Earlier you said The problem is that if I don’t use CASCADE on JPA level the application does not save Page’s objects, because of:
org.springframework.dao.InvalidDataAccessApiUsageException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: net.gocoders.magazineappbackend.entity.Page-95e03344-1de8-5d78-8c3b-4f39ff19d3d5 [new].; nested exception is java.lang.IllegalStateException: During synchronization a new object was found through a relationship that was not marked cascade PERSIST: net.gocoders.magazineappbackend.entity.Page-95e03344-1de8-5d78-8c3b-4f39ff19d3d5 [new].
So I thought that you have added CASCADE and it worked.
Yes, but you told me to do not use CASACADE on JPA level and I remove it.
“First of all, do not use CASCADE on JPA level (in @OneToMany annotation) - it is not needed in CUBA and can cause issues.”
I am trying to achieve all features combined:
Save the pages when saving the issue.
Delete the pages when delete the issue.
Delete the orphan pages when replace the list of pages on issue (orphanRemoval).
I am sorry, I have no intention in be a boring person here.
In fact, an idiomatic way of working with nested collections in CUBA is use of datasources. See the example here: [url=https://doc.cuba-platform.com/manual-6.1/composition_recipe.html]https://doc.cuba-platform.com/manual-6.1/composition_recipe.html[/url]
The datasources send all modified instances to ORM separately and there is no need for CASCADE relationships on JPA level. And probably you will not need your service method too.
Alternatively, you can still try to use JPA CASCADE.
Anyway, would be great if you attach a small project with a part of your data model and a screen where we can reproduce your problem.
Thank you for the help! My intention is to help that amazing platform to grow too.
Here is my project code.
First you need to create an Issue, then use Upload PDF button to upload a PDF file that will be processed and the Pages objects should be created automatic by the service call.
Thank you for the project. It helped me to understand your goal and suggest a solution.
What I have done:
As you have made Issue hard-delete, I removed @OnDelete(DeletePolicy.CASCADE) as useless - it works only for soft-delete entities. So orphanRemoval = true is required for deleting Issues together with Pages.
Created an Issue view that includes the collection of pages:
Page pageObj = new Page();
pageObj.setIssue(_issue);
do not call service - it is no longer needed
in the background task done() method remove existing pages and add new ones to the datasource, then commit the whole DsContext (all datasources will be flushed in one transaction):
public void done(Void result) {
for (Page page : pagesDs.getItems()) {
pagesDs.removeItem(page);
}
for (Page page : pagesList) {
pagesDs.addItem(page);
}
dsContext.commit();
you don’t need to use Vaadin’s safe methods for updating UI in done() and progress() methods - these are called in UI thread, see JavaDocs.
That’s all.
Actually I think you could do the same in the service if you set the reference to Issue in Pages when created them. But I like the solution with datasources more because it keeps all logic in one place. And additional benefit is that you can easily attach UI components to datasources and display results of your processing (Pages) right on this screen.