Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Write sql native query with left join and pagination in hibernate (springboot) [duplicate]

I'm using spring data JPA and I want to write a SQL query in my repository.

I have a following SQL query (notice the LEFT JOIN):

SELECT * FROM institution LEFT JOIN 
(select * from building_institutions where building_institutions.building_id = 1) as reserved_institutions
ON reserved_institutions.institutions_user_id = institution.user_id
WHERE reserved_institutions.institutions_user_id is null;

and i want to execute it in my InstitutionRepository which is as follows:

@Repository
public interface InstitutionRepository extends JpaRepository<Institution, Long>, PagingAndSortingRepository<Institution,Long> {
    // doesn't work
    //@Query("SELECT b.institutions as bi FROM Building b left join Institution i WHERE bi.building_id not in :id")
    Page<Institution> findPotentialInstitutionsByBuildingId(@Param("id") Collection<Long> id, Pageable pageable);

    // doesn't work    
    @Query(
            value = "SELECT * FROM kits_nwt.institution LEFT JOIN\n" +
                    "(SELECT * FROM kits_nwt.building_institutions WHERE kits_nwt.building_institutions.building_id = ?1) AS reserved_institutions\n" +
                    "ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
                    "WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
            nativeQuery = true)
    Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);
}

So, I want to get all institutions which are not in building with certain ID (which I will send as a parameter).

Here is my current DB data:

Institutions:

Institutions

Building institutions:

Building institutions

What I want: (in this query, the id is set on 1, for presentation purposes)

What I want

I have looked at many SO questions and answers (such as this one) but I haven't been able to figure out the solution.

So, how do I write this query so that I get what I want?

Edit 1:

@KevinAnderson Currently, I'm trying with:

@Query(
        value = "SELECT username, password, description, location, title, user_id FROM (institution INNER JOIN user ON institution.user_id = user.id) LEFT JOIN\n" +
                "(SELECT * FROM building_institutions WHERE building_institutions.building_id = 1) AS reserved_institutions\n" +
                "ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
                "WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
        nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);

And I'm getting this exception:

MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'WHERE) FROM building_institutions WHERE building_institutions.building_id = 1) A' at line 2

Edit 2:

@StanislavL here it is:

enter image description here

The problem is that your query gets me the institution with ID 25 because it is in both building with ID 1 and building with ID 2. When you do a JOIN, you have 2 rows ON institution ID being 25 in both institution table and building_institutions table. Then, your WHERE condition removes one from those two rows and I get one row where instituiton ID is 25, and I don't want what.

Here is an image for the above:

Join without WHERE

Edit 3 - this question is not a duplicate because of the following:

  • My query is with pagination (I added "with pagination" to the question title)
  • I'm not using @NamedQuery but @Query
  • My mistake was that I didn't write countQuery parameter to the @Query annotation
like image 907
Filip Savic Avatar asked Jan 26 '18 01:01

Filip Savic


1 Answers

I solved it...

The query needs to look like this:

@Query(
        value = "SELECT * FROM \n" +
                "(institution INNER JOIN user ON institution.user_id = user.id) \n" +
                "LEFT JOIN \n" +
                "(SELECT * FROM \n" +
                "building_institutions \n" +
                "WHERE building_id = :userId)\n" +
                " AS reserved_institutions \n" +
                "ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id \n" +
                " where reserved_institutions.institutions_user_id IS null \n"
                + "ORDER BY ?#{#pageable}"
        ,
        countQuery = "SELECT count(*) FROM \n" +
                "(institution INNER JOIN user ON institution.user_id = user.id) \n" +
                "LEFT JOIN \n" +
                "(SELECT * FROM \n" +
                "building_institutions \n" +
                "WHERE building_id =:userId)\n" +
                " AS reserved_institutions \n" +
                "ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id \n" +
                "where reserved_institutions.institutions_user_id IS null \n" +
                "ORDER BY ?#{#pageable}",
        nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(@Param("userId") Long userId, Pageable pageable);

I was getting the error at line 4 near WHERE, when my query looked like this:

@Query(
        value = "SELECT username, password, description, location, title, user_id FROM (institution INNER JOIN user ON institution.user_id = user.id) LEFT JOIN\n" +
                "(SELECT * FROM building_institutions WHERE building_institutions.building_id = 1) AS reserved_institutions\n" +
                "ON reserved_institutions.institutions_user_id = kits_nwt.institution.user_id\n" +
                "WHERE reserved_institutions.institutions_user_id IS null ORDER BY ?#{#pageable}",
        nativeQuery = true)
Page<Institution> findPotentialInstitutionsByBuildingId(Long userId, Pageable pageable);

and that was because I didn't add the countQuery parameter to the @Query annotation.

Big thanks to all of you who tried to help.

I hope that I save someone else many hours of misery.

Cheers! :)

like image 146
Filip Savic Avatar answered Oct 21 '22 11:10

Filip Savic