12

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
3
  • how (and why) can you join fetch a bag, while just returning the count()?
    – guido
    Commented Feb 19, 2013 at 4:48
  • Hey guido, just wondering why a bag here, every CoApplicant should have only one State and only one Country associated to it. The following pure SQL query for what I want to accomplish works perfectly fine: 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
    – vanvasquez
    Commented Feb 20, 2013 at 17:30
  • 2
    I have the same issue, the problem is with the "Page"ing and "FETCH" if you replace it with List it will work perfectly. did you find the solution, if yes please share it with us :) Commented Feb 4, 2014 at 9:53

1 Answer 1

16

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);
5
  • Thanks a lot joro, what you suggested works perfectly.
    – vanvasquez
    Commented Aug 21, 2014 at 19:14
  • Finally, I found this. Thanks a lot!
    – kerzol
    Commented Nov 6, 2017 at 14:12
  • but we should add a comma between two parameters of @Query
    – kerzol
    Commented Nov 6, 2017 at 14:21
  • Great share... this had me a bit baffled. Commented Jun 7, 2019 at 0:38
  • How can I fetch only some columns from CoApplicant and some columns from ca.country and using CoApplication Entity class? Commented Dec 20, 2019 at 8:33

Not the answer you're looking for? Browse other questions tagged or ask your own question.