Imagine the cacheable JPA Entity :
@Entity
@Table(name = "COUNTRY")
@Cacheable
@Getter
@Setter
public class CountryEntity {
@Id
@Column(name = "CODE")
private String code;
@Column(name = "NAME")
private String name;
}
And this second JPA Entity :
@Entity
@Table(name = "CLUB")
@Getter
@Setter
public class ClubEntity {
@Id
@Column(name = "CODE")
private String code;
@Column(name = "NAME")
private String name;
@ManyToOne
@JoinColumn(name = "FK_COUNTRY")
private CountryEntity country;
}
Here is a very simple instruction that will illustrate my issue :
entityManager.find(ClubEntity.class, "PSG");
Not so long ago, that code would behave like this :
ClubEntity by codeCountryEntity is marked as EAGER, Hibernate would then try to get it
So, I was expecting to only see this SQL query in the logs :
Hibernate: select ce1_0.code,ce1_0.fk_country,ce1_0.name,ce1_0.fk_stadium from club ce1_0 where ce1_0.code=?
I was surprised to observe a different behavior with a more recent version of Hibernate (6.6.29.Final actually) than the ones I was used to have.
In order to prevent a potential select n+1 issue, Hibernate performs a join fetch between the ClubEntity and the CountryEntity entities :
Hibernate: select ce1_0.code,c1_0.code,c1_0.name,ce1_0.name,ce1_0.fk_stadium from club ce1_0 left join country c1_0 on c1_0.code=ce1_0.fk_country where ce1_0.code=?
Although I do understand the reason why Hibernate behaves like this (the idea is to help the developer to get rid of the select n+1 issues), I have no clue how to indicate Hibernate to not perform such an optimization. Because in my case, I mark, on purpose, all cacheable entities as EAGER (I preload them all first). With this "optimization", I have no way to get benefit of the cache since I will always pay the cost of the join in the query, when a no-join query would have done the job easily.
I tried to mark the attribute ClubEntity.country's fetch mode as LAZY, but this doesn't work :
ClubEntity is not loaded, while the value is present in the cache...
Frustrating !My question to you guys is : how to get an attribute typed with a cacheable entity loaded for free by getting the value from the cache, and indicate Hibernate not to perform the useless join ?
Thanks for your help :)
The answer mentioned by Chris is the solution :
@ManyToOne
@Fetch(FetchMode.SELECT)
@JoinColumn(name = "FK_COUNTRY")
private CountryEntity country;
Because the FetchModeType is marked as EAGER, the default FetchMode is JOIN.
That's why in my case, I have to force it to SELECT