Reputation: 932
I have following classes in bidirectional many to many relationship.
@Table(name = "message")
@Entity
public class Message {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "message_id", unique = true, nullable = false)
private int id;
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.MERGE)
@JoinTable(name = "tags_messages",
joinColumns = @JoinColumn(name = "message_id", referencedColumnName = "message_id"),
inverseJoinColumns = @JoinColumn(name = "tag_id", referencedColumnName = "tag_id"))
private Set<Tag> tags=new HashSet<>();
and
@Table
@Entity(name = "tag")
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "tag_id", unique = true, nullable = false)
private int id;
@Column(name = "name", unique = false, nullable = false)
private String name;
@JsonIgnore
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.MERGE)
private Set<Message> messages;
When trying to save new Message
, I got exception saying: "detached entity to persist...Tag". I got it to work by setting CascadeType.MERGE
, but I don't understand why it is working. It would be great if someone can explain me why :)
Steps I did which lead to exception:
In db I already had two Tag
s objects and no Messages
. I also had connecting empty table messages_tags
On frontend (Android) I create new Message
object (without id), add one Tag
(entire object, pulled from db, with id) to Message
.
Send new Message
to backend (using Retrofit). Hit my controller function, then service function in which I tried to save
new Message
with accompanying child Tag
s. At first, my cascading type annotation on both side, was like this:
@ManyToMany(fetch = FetchType.EAGER,cascade = CascadeType.ALL)
I thought, since I have one part of relationship covered, that I need to cover other one as well. So I did this:
newMessage.getTags().forEach(t -> t.getMessages().add(newMessage));
messageRepository.save(newMessage) //Bum! exception
MERGE
as cascading type and save
simply worked. WHY? Are there any other consequences I may experience while doing other CRUD operations on any of these entities?Upvotes: 1
Views: 189
Reputation:
When you add a new Tag to the Message on the frontend, you have a different persistent context from the one used on backend. That's why the tag entity is seen as detached (it has a PK but it is not in the backend's persistent context). Since you did not specify a cascade type for JPA to know that to do with Tag instance, the persist of the Message instance fails.
Once you specify the cascade type MERGE, the Tag instance is merged into the backend's persistent context and the save succeeds.
You can avoid the using of MERGE cascade by saving first the Tag instance.
Upvotes: 2