I am trying to optimize a basic query using JPQL in Spring Data in order to avoid multiple queries to the database and retrieve all of the information in one query using JOIN Fetch and I keep getting this exception:
org.springframework.dao.InvalidDataAccessApiUsageException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=Business.Countries,tableAlias=country1_,origin=BUSINESS.COAPPLICANTS coapplican0_,columns={coapplican0_.Country_Id ,className=com.medifast.entity.core.Country}}] [select count(ca) from com.medifast.entity.core.CoApplicant ca LEFT JOIN FETCH ca.country LEFT JOIN FETCH ca.state where ca.client.id = :clientId]; nested exception is java.lang.IllegalArgumentException: org.hibernate.QueryException: query specified join fetching, but the owner of the fetched association was not present in the select list [FromElement{explicit,not a collection join,fetch join,fetch non-lazy properties,classAlias=null,role=null,tableName=Business.Countries,tableAlias=country1_,origin=BUSINESS.COAPPLICANTS coapplican0_,columns={coapplican0_.Country_Id ,className=com.medifast.entity.core.Country}}] [select count(ca) from com.medifast.entity.core.CoApplicant ca LEFT JOIN FETCH ca.country LEFT JOIN FETCH ca.state where ca.client.id = :clientId]
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:301)
This is my Dao:
/**
* Interface to handle persistence operations for CoApplicants.
*
*/
@Repository
public interface ICoApplicantDao extends JpaRepository<CoApplicant, Long>
{
@Query("select ca from CoApplicant ca JOIN FETCH ca.country c where ca.client.id = :clientId")
Page<CoApplicant> findCoApplicantsByClient(@Param("clientId") Long clientId, Pageable pageable);
}
And these are my entities:
@Entity
@Table(name = "BUSINESS.COAPPLICANTS")
@SQLDelete(sql = "UPDATE BUSINESS.COAPPLICANTS SET DELETED = 1 WHERE id = ?")
@Where(clause = "DELETED <> 1")
@Data
@EqualsAndHashCode(callSuper = true)
public class CoApplicant extends AbstractAuditableEntity
{
/**
* Serial.
*/
private static final long serialVersionUID = -297231024073091062L;
/**
* First name.
*/
@Column(name = "FirstName")
private String firstName;
/**
* Last name.
*/
@Column(name = "LastName")
private String lastName;
/**
* Recognition Name.
*/
private String recognitionName;
/**
* For the address line 1 field.
*/
private String addressLine1;
/**
* For the address line 2 field.
*/
private String addressLine2;
/**
* City.
*/
private String city;
/**
* State.
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "State_Id")
private State state;
/**
* Zip Code.
*/
private String zipCode;
/**
* Country.
*/
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "Country_Id")
private Country country;
/**
* Email address.
*/
@Column(unique = true)
private String email;
/**
* Main Phone number.
*/
private String mainPhone;
/**
* Constructor.
*/
public CoApplicant()
{
super();
}
}
/**
* Country entity.
*/
@Entity
@Table(name = "Business.Countries")
@SQLDelete(sql = "Update Business.Countries set deleted = 1 where id=?")
@Where(clause = "deleted <> 1")
@Data
@EqualsAndHashCode(callSuper = true)
public class Country extends AbstractAuditableEntity
{
/**
* Serial.
*/
private static final long serialVersionUID = -267110442898674427L;
/**
* Name of the country.
*/
private String name;
/**
* The orders for the client.
*/
@OneToMany(mappedBy = "country", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Collection<State> states;
/**
* Const.
*/
public Country()
{
super();
}
}
/**
* State entity.
*/
@Entity
@Table(name = "Business.States")
@SQLDelete(sql = "Update Business.States set deleted = 1 where id=?")
@Where(clause = "deleted <> 1")
@Data
@EqualsAndHashCode(callSuper = true)
public class State extends AbstractAuditableEntity
{
/**
* Serial.
*/
private static final long serialVersionUID = 8643487990581006632L;
/**
* Name of the state.
*/
private String name;
/**
* Code of the state.
*/
private String code;
/**
* Country to which this State belongs.
*/
@ManyToOne
@JoinColumn(name = "Country_Id")
private Country country;
/**
* Constr.
*/
public State()
{
super();
}
}
Any insights, help would be highly appreciated.
The pure SQL for what I want to accomplish would look something like this:
SELECT ca.Id
,firstName
,lastName
,recognitionName
,addressLine1
,city
,email
,mainPhone
,coun.name
,sta.name
FROM coApplicants AS ca
LEFT JOIN countries AS coun
on ca.country_Id=coun.id
LEFT JOIN states AS sta
on ca.state_Id=sta.id
Using a pageable query with JOIN FETCH
is an issue for Hibernate.
To solve the situation, you have to place countQuery
- almost the same as original, but unrelated INNER JOINs (eventually FETCHes) are removed.
Source of the solution could be found here: Spring-Data FETCH JOIN with Paging is not working
So the solution for you will be:
@Query(
value="select ca from CoApplicant ca JOIN FETCH ca.country c where ca.client.id = :clientId",
countQuery="select count(ca) from CoApplicant ca where ca.client.id = :clientId")
Page<CoApplicant> findCoApplicantsByClient(@Param("clientId") Long clientId, Pageable pageable);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With