Eager fetching with multiple selects but without FetchType.EAGER annotation

2.1k Views Asked by At

I want to eagerly fetch one-to-many List-s in some use cases (but not always, so @OneToMany(fetch=FetchType.EAGER) won't be good), and I also want JPA to use separate SQL selects to fetch those List-s (one select per List, not one per List item), instead of an SQL join. That's because of the Cartesian products (I have two independent lists).

You may think that I could use a JPA 2.1 entity graph (that's what it's for after all, marking attributes as eager on an ad-hoc manner) or less elegantly left join fetch with JPQL (or with criteria API). But these seem to stick to using SQL join (not surprisingly in the case of left join fetch), and even if thus they end up with a MultipleBagFetchException, they don't fall back to fetching some of the List-s in separate SQL selects. Actually my case is a multiple-bags case as well, but before you redirect me to working it around with Set-s and such, note that the premise is that I don't want joins (too big Cartesian product). Now, just to tease me, if I annotate the List attributes with @OneToMany(fetch=FetchType.EAGER), Hibernate will be smart enough to fetch the List-s in separate selects to avoid the multiple bags (plus I can even force this behavior with Hibernate's Fetch annotation). So it seems JPA 2.1 entity graphs fail to deliver the promise of ad-hoc FetchType.EAGER, as they behave rather like ad-hoc fetch joins, which we already had before them too. Or do I missing something?

It occurred to me that I should just call parentEntity.getSomeList().size() after the initial query that this time wouldn't try to eager fetch the lists, but problem is, I also need to eagerly fetch some attributes of the list items themselves, and I can't specify an entity graph for the above call. I also could create a JPA query myself, which only gets "someList" with said eager fetches (no problem with those), but then how can I put that resulting list back into the parentEntity? If I call parentEntity.setSomeList(fetchedList), parentEntity becomes dirty (despite that it's not on the owner side of the association), and so it will be SQL update-d (only to spoil the last modification time and optimistic locking version).

1

There are 1 best solutions below

1
On

An alternative it's mapping the same table in two different entities. So, when you need the EAGER behaviour with multiple selects, you can use the entity with:

@Fetch(FetchMode.SELECT)
@ManyToOne(fetch = FetchType.EAGER)

And when you want the lazy, you use the another entity with:

@Fetch(FetchMode.SELECT)
@ManyToOne(fetch = FetchType.LAZY)