Couple of days back we are facing a performance issue while object creation. It is taking too long to create a
object. While investigation we found that hibernate is deleting all entries and re-inserting
them back in the join table of a many to many relation.
After spending couple of hours with generated SQL analysis, we found that if we use list in the many side of ORM relation hibernate behaves strange. While adding or removing an item from the list, hibernate issues a delete statement to delete all elements, and then issue insert statements to insert everything back with additional entry of newly added object or except the one removed. This seems to be inefficient and odd. Why not just delete or add the one item? Then if we change the collection type to a Set, only one delete or add statement is issued by itself.
The scenario described above occurs when mapping a many to many or a one to many relationship with a association table. The association table stores two attributes, each of which is a foreign key to another table. In addition, the many side object is mapped with a List, but no index column is used.
If table does not have an index column, and List is used as the property type, this property is mapped as a Hibernate bag. A bag does not retain its order when it is retrieved from the database, but it can be optionally sorted or ordered.
After spending couple of hours with generated SQL analysis, we found that if we use list in the many side of ORM relation hibernate behaves strange. While adding or removing an item from the list, hibernate issues a delete statement to delete all elements, and then issue insert statements to insert everything back with additional entry of newly added object or except the one removed. This seems to be inefficient and odd. Why not just delete or add the one item? Then if we change the collection type to a Set, only one delete or add statement is issued by itself.
The scenario described above occurs when mapping a many to many or a one to many relationship with a association table. The association table stores two attributes, each of which is a foreign key to another table. In addition, the many side object is mapped with a List, but no index column is used.
If table does not have an index column, and List is used as the property type, this property is mapped as a Hibernate bag. A bag does not retain its order when it is retrieved from the database, but it can be optionally sorted or ordered.
Hibernate's internals
If we are using List as a mapped attribute in hibernate without indexed column, hibernates treats it as a Bag. Since Hibernate handles List as a Bag (Unordered collection with non unique values. The best feature of a bag is that you can get the number of occurrences of an object through the API With a list, there is no way to do the same without iterating through the whole list.) as soon as we delete and add a element in this collection. Hibernate issues a SQL to delete all the elements first from join table which are no supposed to be deleted and then it re-insert all of them back from the Bag.Final Thought
When using a list in hibernate an indexed column is necessary, if we want efficient manipulation of adding or deleting items. With an indexed column, when we code a remove(index), hibernate can relate that index to the column in the db or or add(element) will generate only one insert statement even if there are duplicates, it can distinguish and delete one row or add one row based on operation.Expensive but better yet, if duplicates are not needed in the application, Set can be used. Set does not require index column and hibernate can easily distinguish one row because it know a set does not have duplicates.
2 Comments
Hello.
ReplyDeleteI found this article because same issue was happening to me. Well... not really an issue, but doubt about Hibernate behavior.
But, after some research and test-error research with a real Hibernate development environment, I have found out that what makes Hibernate to behave as explained in this article (delete-insertions aproach) is not the existence of indexed columns.
The following lines explain what I found.
It depends on which object is added to which.
Let's call object A the one that makes the addition and object B the one that is added.
I.E., in a generic way:
objectA.add(objectB);
If object A already exists in database, it somehow needs to delete all of the relations and re-insert the ones needed.
If object A is new, it will perform a clean insert in the database.
The existence or not of indexes in one or other columns didn't change Hibernate behavior in my tests. No matter what columns of the join table had the indexes, that Hibernate did exactly the same and responding only to what explained at the begining of this comment.
Open to further clarifications: pgbonino@gmail.com
Amazing explanation.....SOLVED.
ReplyDeletePost a Comment