My entity looks like below: to simplify I am just showing two fields. We decided to do fetch all to load all properties at once.
@Entity
public class Person{
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<PhoneNumber> phoneNumbers = new HashSet<>(0);
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "AGENCY_ID")
private Agency agency;
left join:(This returns duplicate records.)
select person
from Person person left join person.agency ag left join person.phoneNumbers
where upper(person.name) LIKE '%SU%' and upper(person.status.rcode) = 'ACTIVE'
order by person.agency.name asc, person.id asc
left join with distinct : This gives "ORA-01791: not a SELECTed expression" because i am using agency field in order clause.
select distinct person
from Person person left join person.agency ag left join person.phoneNumbers
where upper(person.name) LIKE '%SU%' and upper(person.Status.rcode) = 'ACTIVE'
order by person.agency.name asc, person.id asc
left join fetch : This works fine, no duplicates . but has big performance hit for pulling 2000 person records. taking about 15 seconds vs 1 with just left join.
select person
from Person person left join fetch person.agency ag left join fetch person.phoneNumbers
where upper(person.name) LIKE '%SU%' and upper(person.Status.rcode) = 'ACTIVE'
order by person.agency.name asc, person.id asc
fetch all properties: No duplicates. performs better.But.. When I try to query a person like below (this person doesn't have any agency): it returns the person record (that means it is doing a left join).
select person
from Person person
fetch all properties
where upper(person.name) LIKE '%SU%' and upper(person.status) = 'ACTIVE'
order by person.id asc
But when I do below, I don't get the person record back. (Difference is added field of agency in order by clause, seems like it is not doing left join in this case.)
select person
from Person person
fetch all properties
where upper(person.name) LIKE '%SU%' and upper(person.status) = 'ACTIVE'
order by person.agency.name asc, person.id asc
What i am looking is to avoid duplicates, sortable by all fields of person with good performance.
The navigation path syntax (
person.agency
) is always translated into an inner join, that's why persons with no agency are not included in the result set.You have to explicitly write an outer join to avoid the implicit inner join:
EDIT
distinct
in theleft join
case does not work because it does not make sense to order by columns that are not included in the columns mentioned in theselect distinct
clause.If you want to eagerly load lazy associations in the query, then you have no choice but to do
left join fetch
. But duplicates are returned also. To eliminate them, simply add the returned list to aLinkedHashSet
to preserve the returned order:Regarding the bad performance of this approach, Hibernate has nothing to do with it. You maybe have some eagerly loaded entities in the entities that are left-join-fetched (thus suffering from the n+1 selects problem), or the duplicated entities are really large (thus making and transferring a large result set).
You may want to use
@BatchSize
instead of left-join-fetching collections to avoid performance issues.