使用QueryDsl的多态where子句

Eti*_*veu 9 java querydsl

我正在尝试使用QueryDsl编写带有多态where子句的查询.

由于在摘要中解释我想做什么有点困难,我克隆了spring-boot-sample-data-jpa项目并修改它以显示我正在尝试做的一个例子.

我有这些模型类,你会注意到SpaHotelSportHotel扩展Hotel实体.

我正在尝试编写一个返回包含a SpaHotel或a SportHotel主要运动属于给定类型的所有城市的查询.

我写了一个该查询JPQL版本,这有点难看(我不喜欢这sport is null部分来表示它是一个Spa酒店),但似乎回归了我想要的.

该查询的QueryDsl版本似乎不起作用:

public List<City> findAllCitiesWithSpaOrSportHotelQueryDsl(SportType sportType) {
  QCity city = QCity.city;
  QHotel hotel = QHotel.hotel;

  return queryFactory.from(city)
      .join(city.hotels, hotel)
      .where(
          hotel.instanceOf(SpaHotel.class).or(
              hotel.as(QSportHotel.class).mainSport.type.eq(sportType)
          )
      ).list(city);
}
Run Code Online (Sandbox Code Playgroud)

我的测试失败了:

test_findAllCitiesWithSpaOrSportHotelQueryDsl(sample.data.jpa.service.CityRepositoryIntegrationTests)  Time elapsed: 0.082 sec  <<< FAILURE!
java.lang.AssertionError:
Expected: iterable over [<Montreal,Canada>, <Aspen,United States>, <'Neuchatel','Switzerland'>] in any order
     but: No item matches: <Montreal,Canada> in [<Aspen,United States>, <'Neuchatel','Switzerland'>]
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:20)
    at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:8)
    at sample.data.jpa.service.CityRepositoryIntegrationTests.test_findAllCitiesWithSpaOrSportHotelQueryDsl(CityRepositoryIntegrationTests.java:95)
Run Code Online (Sandbox Code Playgroud)

似乎我的查询没有返回"蒙特利尔",应返回,因为它包含一个SpaHotel.

另外,我想知道QueryDsl将查询转换为交叉连接是否正常:

select city0_.id as id1_0_, city0_.country as country2_0_, city0_.name as name3_0_
from city city0_
inner join hotel hotels1_
on city0_.id=hotels1_.city_id
cross join sport sport2_
where hotels1_.main_sport_id=sport2_.id and (hotels1_.type=? or sport2_.type=?)
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 为什么该查询不返回"蒙特利尔",其中包含一个SpaHotel?
  2. 有没有更好的方法来编写该查询?
  3. 生成的SQL执行交叉连接是正常的吗?我不能像在JPQL中那样进行左连接吗?

Tim*_*per 4

JPQL 查询的正确转换

String jpql = "select c from City c"
    + " join c.hotels hotel"
    + " left join hotel.mainSport sport"
    + " where (sport is null or sport.type = :sportType)";
Run Code Online (Sandbox Code Playgroud)

return queryFactory.from(city)
  .join(city.hotels, hotel)
  .leftJoin(hotel.as(QSportHotel.class).mainSport, sport)
  .where(sport.isNull().or(sport.type.eq(sportType)))
  .list(city);
Run Code Online (Sandbox Code Playgroud)

在您的原始查询中,此属性的用法

hotel.as(QSportHotel.class).mainSport
Run Code Online (Sandbox Code Playgroud)

导致交叉连接并将查询限制为 SportHotels。

Querydsl 仅对仅在查询的 orderBy 部分中使用的路径使用隐式左联接,一切都会导致隐式内联接。