Reputation: 1246
I'm currently developing a web ticket system, and have classes to store my ticket data. Every ticket can have multiple labels associated with it, and to manage those labels I created a label editor. This works just fine, only a delete option is still missing. Until now most deletes failed with the explaination that the label was still referenced to by another ticket, which required removing it first. In the process of finding a solution I came across CascadeType.DELETE, which seemed to do exactly what I was after.
However, since the ticket object contains a set of labels and not the other way round, every time I deleted a ticket, all the labels would disappear, instead of the other way round. After reading the docs for the CascadeType, this seemed obvious, but I am now clueless how to achieve the opposite effect. I thought of just creating a reference to all the tickets that are using my Label, although that seems like storing the same data twice.
My Ticket looks something like this:
@Entity
@Data
@NoArgsConstructor
public class Ticket {
@Id
@Column(length = 40)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
@Column(nullable = false)
private String title;
@OneToMany(targetEntity = TicketEntry.class, mappedBy = "ticket", orphanRemoval = true, fetch = FetchType.EAGER)
private List<TicketEntry> entries = new ArrayList<>();
@Column
private Status status = Status.OPEN;
@ManyToMany(fetch = FetchType.EAGER)
@JoinTable(name = "ticket_labels",
joinColumns = { @JoinColumn(name = "ticket_id")},
inverseJoinColumns = { @JoinColumn(name = "label_id" )}
)
private Set<TicketLabel> labels = new HashSet<>();
}
And the labels like this:
@Entity
@Data
@NoArgsConstructor
public class TicketLabel {
@Id
@Column(length = 40)
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name = "";
@EqualsAndHashCode.Exclude
@ToString.Exclude
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.REMOVE)
@JoinTable(name = "ticket",
joinColumns = { @JoinColumn(name = "label_id")},
inverseJoinColumns = { @JoinColumn(name = "ticket_id" )}
)
private Set<Ticket> tickets = new HashSet<>();
}
but that still does not work. Do I even need a reference to all my Tickets in my Label object? Or can I somehow use a "reversed REMOVE" cascade type?
Upvotes: 1
Views: 67
Reputation: 1475
Your many-to-many mapping means that you have two intersection tables ticket and ticket_labels. I bet you don't want that, you need just one. For bidirectional mapping you may have:
public class TicketLabel {
//...
@ManyToMany(mappedBy = "labels", cascade {CascadeType.MERGE, CascadeType.PERSIST})
private Set<Ticket> tickets = new HashSet<>();
}
//...
and
public class Ticket {
// ...
@ManyToMany
@JoinTable(name = "ticket_labels",
joinColumns = { @JoinColumn(name = "ticket_id")},
inverseJoinColumns = { @JoinColumn(name = "label_id" )}
)
private Set<TicketLabel> labels = new HashSet<>();
public void removeLabel(Label label) {
labels.remove(label);
label.getTickets().remove(this);
}
public void addLabel(Label label) {
labels.add(label);
label.getTickets().add(this);
}
// ...
}
You have to manually synchronize labels and tickets by helper remove and add methods as explained in this thread. To remove label you have to remove it from all the tickets. CascadeType.REMOVE will cause in this case the removal of the whole association.
Upvotes: 1