I have faced pretty doubtful Hibernate behaviour when initializing batches of lazy collections with preconfigured batch-size.
Having the following mapping:
Code:
<class name="Person">
<set name="cats" batch-size="1000">
...
</set>
</class>
and reading a list of 135 Person from database I was expecting, that invoking Hibernate.initialize() on personList.get(0).getCats() will initialize all the 135 lazy cat collections. Surprisingly, Hibernate initialized only 125 of 135 cat collections with this invocation. I was pretty confused by this behaviour, as my configured batch size for cat collection - 1000 was greater than 135, so I expected only one query to be ran to initialize all the lazy cat collections in my person list.
After debugging Hibernate code, it turned out, that org.hibernate.loader.collection.BatchingCollectionInitializer uses some "intelligent" small batch slicing, operating with "smallBatch" term. By default it uses the following array of batch sizes [1000, 500, 250, 125, 62, 31, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1], so when Hibernate is asked to initialize some proxy that uses batch loading, it looks for the first predefined batch size from given list, that is less than the actual number of proxies to be initialized. In my case it was 125, so I got my two queries executed (125 and then 10).
This logic is really confusing, as this slicing interfers with actual number of proxies, thus executing more than one query for initializing the number of proxies, that could potentially be initialized with a single query.
The question is: why does Hibernate behave this way? If batch loading was introduced with the purpose of optimization, then why does Hibernate still execute redundant queries that can be avoided?