Hibernate @ManyToMany with FetchMode.JOIN and FetchType.EAGER generates secondary queries

I have an existing web app using Hibernate 5.1. I am in the process of upgrading to 5.3.7. I noticed the deprecated warnings on my Criteria queries so I converted them to use JPA criteria. I have an entity that has a @ManyToMany mapping. This property was a list so I was getting a subselect on queries. I converted my code so that it was now a Set and annotated with @Fetch(FetchMode.JOIN) but still am getting subselect behavior.

I have it annotated as:

    @ManyToMany( cascade = { CascadeType.DETACH, CascadeType.MERGE, CascadeType.PERSIST, CascadeType.REFRESH }, fetch = FetchType.EAGER, targetEntity = com.mydomain.model.Role.class)
    @JoinTable(name = "user_role", joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "role_id"))
	@Fetch(FetchMode.JOIN)

Is there any reason I cannot get JOINs on my @ManyToMany properties with JPA criteria based queries?

Using @Fetch(FetchMode.JOIN) is not needed since you are using FetchType.EAGER already. However, using eager loading is a very bad idea from a performance perspective.

More, using FetchType.EAGER or FetchMode.JOIN does not guarantee that Hibernate will always employ a JOIN to fetch the entity. A JOIN will be used only if you fetch the entity directly via entityManager.find. However, for JPQL or Criteria API, unless you use JOIN FETCH, a join will not be used and you will see secondary queries and bump into N+1 query issues.

Is there any reason I cannot get JOINs on my @ManyToMany properties with JPA criteria based queries?

Yes, there is a much better way than using FetchType.EAGER. You need to use JOIN FETCH in your JPQL or Criteria API queries. For more details, check out this article.