views:

338

answers:

2

I have written a very basic and naive oneToMany relationship between a ChatComponent and its Chat Messages like this:

@OneToMany
List<ChatMessage> chatMessages;

This basically works, i.e., doing something like:

ChatMessage chatMessage = vo.toDomainObject();
chatMessage.setDate(new Date());
//Add the message to the chat component
em.getTransaction().begin();
em.persist(chatMessage);
chat.addChatMessage(chatMessage);
em.persist(chat);
em.getTransaction().commit();

gets the job done. Only, looking at the SQL logs, I can see that every time, the entire collection of chat messages is persisted again. That's obviously something I can't afford, given chat messages could quickly add up in the thousands.

The SQL that gets repeated for each chat message is as follows:

Hibernate: insert into BaseComponent_ChatMessage (BaseComponent_id, chatMessages_id) values (?, ?)

preceded by:

Hibernate: delete from BaseComponent_ChatMessage where BaseComponent_id=?

From this I conclude that Hibernate doesn't have a way to know that we're not dealing with a whole set of new objects and that it should keep those it already has.

I'm sure there is a way to add (and persist) only one member of the many side of a relationship, but I can't seem to find out how.

+1  A: 

Override hashCode and equals methods of your ChatMessage class, so that Hibernate can know that these are the same objects.

Use your IDE to generate hashCode() and equals(). In Eclipse it is right click > Source > Generate hashCode and equals, and choose the property which is @Id for your entity.

Also, try adding the cascade=CascadeType.ALL attribute to @OneToMany

Bozho
Hi Bozho,That makes good sense indeed. However, I can see that the first statement issued is: Hibernate: delete from BaseComponent_ChatMessage where BaseComponent_id=?...which seems to indicate that it's the ChatComponent (ChatComponent inherits from BaseComponent here) that's not recognised as already existing.I have tried implementing hashCode() in both classes by just letting it return the id of the entity. I'm not sure this is the way to go though, and I'm a bit confused about what to do in the equals method aswell.Thank you however, this definitely seems to be a good lead :-)
check my update
Bozho
Mmmh, I've tried every possible combination of generating hashCode and equals on both ends of the relationship, using Eclipse, but I'm still having n+1 inserts :(thanks anyway!
try adding the cascade=CascadeType.ALL attribute
Bozho
+1  A: 

I found that the only way to avoid the entire list of chat messages to be persisted each time is to establish a bidirectional relationship between the chat component and its chat messages

@OneToMany(cascade={CascadeType.PERSIST,CascadeType.MERGE}, fetch=FetchType.LAZY, mappedBy="parent")
List<ChatMessage> chatMessages;

@ManyToOne
@JoinColumn(name="OWNER_CHAT_COMPONENT_ID", nullable=false)
private ChatComponent parent;

then in the addChatMessage function I can simply add a message to the chat component without having to persist it, then persist the chat component and that works beautifully.

 em.getTransaction().begin();
 chat.addChatMessage(chatMessage);
 em.persist(chat);
 em.getTransaction().commit();