David Artmann David Artmann - 11 days ago 5
Java Question

Hibernate: how to set relations (annotations) in a more complex model?

I have the following simplified model:

Business - (1:n) - Assignment - (n:1) - Process


The model classes have the following annotation:

Business

@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "business", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Assignment> assignments;


Assignment

normally one would avoid creating a separate model class here, because Business and Process have a n:m relation. But I need to add attributes to Assigment itself.

@ManyToOne
@JoinColumn(name = "business_id")
private Business business;

@ManyToOne
@JoinColumn(name = "process_id")
private Process process;


Process

@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "process", cascade = CascadeType.ALL)
private List<Assignment> assignments;


Requirements


  1. When a Business or Process is deleted, I also want all his Assignments deleted (but not the partner of the relation on @OneToMany side)

  2. When an Assignment is deleted, I do not want to remove both @OneToMany sides (either Business or Process)



Hints


  • I tried this with
    orphanRemoval = true
    and without, but got no complete sufficient solution

  • The model classes inherit from a MappedSuperClass which provides Identifier

  • The
    @LazyCollection(LazyCollectionOption.FALSE)
    was needed because I have several
    @OneToMany
    relations in a Business and Process but this annotation does not relate to this issue

  • I use H2 as database and only work with Spring's @Repository interfaces when interacting with persistance layer so not a single line of SQL is written






UPDATE

Unfortunately I thought that the endorsement of following annotation:
@OnDelete(action = OnDeleteAction.CASCADE)

and JUnit test approves correct working of desired behaviour hence answered my question.

@Test
public void test() {
// stores an Business object in db and returns the saved object
Business b = createBusiness();
// stores an Process object in db and returns the saved object
Process p = createProcess();
// stores Assignmnent object with both relations in db and returns the saved object
Assignment a = createAssignment(b, p);
assertThat(a).isNotNull();
// deletes Process object from db
processService.delete(p);
assertThat(processService.getById(p.getId())).isNull();
assertThat(assignmentService.getById(a.getId())).isNull();
assertThat(businessService.getById(b.getId())).isNotNull();
}


But this is not the case. In my JavaFX application the deletion is logged and it looks like its working, but when querying the database afterwards, the entity is still in the table although in the JUnit test it is not...
If anybody could bring some light in this issue I would be very thankful.

If any further information is needed, I will provide it, of course. Thank you very much in advance for helping me out.

Answer

EDIT Finally I have solved the issue and got my desired behaviour with the following setup:

Business

@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "business", orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Assignment> assignments;

Assignment

@ManyToOne
@JoinColumn(name = "business_id")
private Business business;

@ManyToOne
@JoinColumn(name = "process_id")
private Process process;

Process

@LazyCollection(LazyCollectionOption.FALSE)
@OneToMany(mappedBy = "process", orphanRemoval = true)
@OnDelete(action = OnDeleteAction.CASCADE)
private List<Assignment> assignments;

Please take note of the added annotation @OnDelete(action = OnDeleteAction.CASCADE). This hint came from here. I omitted Hibernate docs here, because they (imho) do not provide additional useful informations about the feature than the linked SO post.

Update: Also consider the removed cascade attribute, which was not necessary because I am using hibernates @OnDelete.

Comments