Recently, we ran into a severe performance bottleneck during object manipulation. Profiling the database calls revealed something terrifying: when adding a single item to a Many-to-Many collection, Hibernate was executing a DELETE statement wiping out every entry in the join table for that entity, followed by a flurry of INSERT statements to write everything back — plus the new item.
If you have 1,000 items in the collection and add one, Hibernate deletes 1,000 rows and inserts 1,001.

The Root Cause: The Unindexed List

This anomaly occurs when you map a @ManyToMany or a @OneToMany (backed by a join table) using a java.util.List without explicitly defining an index column.
Under the hood, if there is no index column, Hibernate treats the List as a Bag. A Bag is an unordered collection that allows non-unique values. Because it doesn't track order and allows duplicates, Hibernate has no primary key or index to uniquely identify a specific row in the join table.
When you modify the collection, Hibernate cannot safely issue a targeted DELETE or INSERT. To guarantee the database state matches your in-memory Java object, it resorts to the nuclear option: drop all associations and recreate them from scratch.

The Fix

You have two architectural choices to resolve this, depending on your domain constraints.
The Problematic Code (The Trap)
The Problematic Code
Solution 1: Use a Set (Recommended)
If your domain logic does not require duplicate entries, simply change the collection to a Set. Hibernate knows a Set has unique elements, allowing it to uniquely identify rows and issue a single targeted INSERT or DELETE statement.
Solution 1: Use a Set
Solution 2: Use an Indexed List
If the business logic dictates that you absolutely must maintain strict ordering and allow duplicates, you must provide an index column. Adding @OrderColumn gives Hibernate a physical column in the database to track exactly which index was added or removed, preventing the table wipe.
Solution 2: Use an Indexed List
A quick tip on architecture: When designing your domain model, don't blindly default to List just because it is familiar in Java. ORM collection semantics dictate database performance. If you need efficient manipulation of ordered items in Hibernate, an indexed column is strictly necessary. Better yet, challenge the business requirement: if duplicates aren't genuinely needed, always default to a Set.

Final Thought

When using a List in Hibernate, an indexed column is necessary if you want efficient manipulation of adding or deleting items. With an indexed column, Hibernate can relate the index to the column in the database and issue only one targeted statement based on the operation.
If duplicates are not needed in your application, Set is the better choice. Set does not require an index column, and Hibernate can easily distinguish one row because it knows a set does not have duplicates.
In Hibernate, treating a List like a Set without telling the database is the fastest way to turn a one-millisecond update into a database coffee break.