1

Problem statement: CRUD a Message with multiple Categories without the CRUD operations affecting the Category.

I've got two tables: Category and Message. The Category table is intended to be specific to the user account and will be shared across several features, similar to 'tags'. The Message table is specific to a feature and is tagged with 0..* Categories. The Category table doesn't require any knowledge of which feature is referencing it so I've created an associative table to store the relations, Cat_Msg, which has references the PK of each table.

|---------|
|Category |
|_________|
     |
     |
|--------|
|Cat_Msg |
|________|
     |
     |
|--------|
|Message |
|________|

The issue I'm having is in the Message model for Hibernate. I either get this exception:

org.hibernate.TransientObjectException: object references an unsaved transient instance

Or if I add the cascade = CascadeType.ALL to the Message annotation it then deletes the Category when deleting the Message.

I think the issue is similar to this but I've got the issue where the Category doesn't have entries for the Message so I can't swap ownership. Or this site, but I don't want the associative table data, I need the Category.

Code example:

@XmlRootElement
@Entity(name = "category")
public class Category {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String name;
}

@XmlRootElement
@Entity(name = "message")
public class Message {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;
    private String message;
    @OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JoinTable(name = "cat_msg", joinColumns = { @JoinColumn(name = "cat_id", referencedColumnName = "id") },
        inverseJoinColumns = { @JoinColumn(name = "msg_id", referencedColumnName = "id") })
    private Collection<Category> categories;
}

I realise the CascadeType.ALL is causing the deletion of Category entries but I don't know how to resolve it so as not to get the hibernate exception and achieve what I need.

Community
  • 1
  • 1
fakataha
  • 785
  • 6
  • 31
  • 1
    I'm not sure I understand the question correctly, but wouldn't `{CascadeType.PERSIST, CascadeType.MERGE}` instead of `CascadeType.ALL` do the trick? Also, didn't you mean to use `@ManyToMany`? – crizzis Feb 23 '17 at 22:25
  • Do I need @ManyToMany? My thinking is that one Message can have multiple Categories == @OneToMany. I've looked for descriptions of the CascadeTypes but its unclear to me what each accomplish. I'll give your suggestion a go! – fakataha Feb 23 '17 at 22:30
  • The question is whether one category contains multiple messages or just one – crizzis Feb 23 '17 at 22:33
  • the category has no knowledge of the messages. a message can reference multiple categories however. think of it similarly to how SO allows the tagging of a question. if a question is deleted it shouldn't delete the tag, for example, and there isn't a need for the tag to know about where its being used. – fakataha Feb 23 '17 at 22:36
  • It has nothing to do with the directionality of the association. The tags on SO have knowledge of the questions, in the sense that clicking a tag will reveal a list of tagged questions. You need many-to-many if you're going to reuse the `Category` entity in more than one message (as opposed to creating a new `Category` entity with the same name). JPA will not allow you to do the former with `@OneToMany`, that's why I'm asking – crizzis Feb 23 '17 at 22:41
  • Also, if you decide to go with `@OneToMany`, it would seem that categories do not require their own persistent identity as per your data model. If they also happen to be represented by simple strings, I'd consider using an `@ElementCollection` with a list of strings instead. However, your concern for deleting a message also unnecessarily removing categories suggests `@ManyToMany` is what you wanted in the first place. – crizzis Feb 23 '17 at 22:48
  • @atamakosi Perhaps if you provided an example of what you are looking for with a populated table or a simple use case? The deletion behavior you are seeing on CascadeType.ALL is expected and I do not see an explanation of this exception you are getting otherwise. – Will Dazey Feb 23 '17 at 23:10
  • @crizzis After testing your first suggestion, it looks like I have what I was after. I had a logic mistake in adding Categories but after that its working well. The Message can be deleted without deleting the Category AND the associative table Cat_Msg gets the correct entry removed as well. Thanks! – fakataha Feb 23 '17 at 23:11

0 Answers0