spring-data-jpa 当与X列的关系为空时,在HQL语句中不选择记录,(PostgreSQL)

nhhxz33t  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(129)
@Query("SELECT new com.api.projection.GarageVehicleRequestLookupProjection" +
        "(vr.id, vr.appUser.fullName, vr.appUser.unit.id, vr.appUser.unit.name, vr.createdAt, " +
        "vr.startAt, vr.endAt, vr.destination,vr.reason, vr.assignedVehicle.id, " +
        "vr.assignedVehicle.plate, vr.latestAction.vehicleRequestStatus,vr.appUser.unit.director.id, vr.appUser.unit.director.fullName,vr.driverRequested," +
        "vr.garage.name, vr.vehicleTypeRequested, vr.requestType, vr.description, vr.assignedVehicleDriver.id, vr.assignedVehicleDriver.fullName)" +
        " FROM VehicleRequest AS vr " +
        "WHERE " +
        "vr.appUser.fullName like %:fullName% " +
        "AND (coalesce(:unitIds, null) is null or vr.appUser.unit.id in :unitIds) " +
        "AND (coalesce(:garageIds, null) is null or vr.garage.id in :garageIds) " +
        "AND (coalesce(:vehicleIds, null) is null or vr.assignedVehicle.id in :vehicleIds) " +
        "AND (coalesce(:statuses, null) is null or vr.latestAction.vehicleRequestStatus in :statuses) " +
        "AND (coalesce(:createdAt, null) is null or cast(:createdAt as date) = cast(vr.createdAt as date)) " +
        "AND vr.startAt >= :startAt and vr.startAt <= :startAtEnd ")
Page<GarageVehicleRequestLookupProjection> getGarageVehicleRequestsWithFilter(
        @Param("fullName") String fullName,
        @Param("unitIds") List<Long> unitIds,
        @Param("garageIds") List<Long> garageIds,
        @Param("vehicleIds") List<Long> vehicleIds,
        @Param("statuses") List<VehicleRequestStatus> status,
        @Param("createdAt") Date createdAt,
        @Param("startAt") Date startAt, @Param("startAtEnd") Date startAtEnd,
        Pageable pageable);

VehicleRequest表中有146条记录。当我运行上面的hql语句时,如果vr.assignedVehicle为空,则不会返回记录,因此我得到36条记录,因为只有这些记录具有assignedVehicle(assignedVehicleId)。
当我不像这样做投影的时候,

SELECT vr FROM VehicleRequest AS vr ...

所有的记录都将被返回。上述方法的问题是有太多的连接,因此JPA对数据库发出太多的请求。
这是在删除了一些不必要的条件后生成的语句,

select vehiclereq0_.id                         as col_0_0_,
       appuser1_.full_name                     as col_1_0_,
       appuser1_.unit_id                       as col_2_0_,
       unit4_.name                             as col_3_0_,
       vehiclereq0_.created_at                 as col_4_0_,
       vehiclereq0_.start_at                   as col_5_0_,
       vehiclereq0_.end_at                     as col_6_0_,
       vehiclereq0_.destination                as col_7_0_,
       vehiclereq0_.reason                     as col_8_0_,
       vehiclereq0_.assigned_vehicle_id        as col_9_0_,
       vehicle5_.plate                         as col_10_0_,
       vehiclereq6_.vehicle_request_status     as col_11_0_,
       unit4_.director_id                      as col_12_0_,
       appuser11_.full_name                    as col_13_0_,
       vehiclereq0_.driver_requested           as col_14_0_,
       garage12_.name                          as col_15_0_,
       vehiclereq0_.vehicle_type_requested     as col_16_0_,
       vehiclereq0_.request_type               as col_17_0_,
       vehiclereq0_.description                as col_18_0_,
       vehiclereq0_.assigned_vehicle_driver_id as col_19_0_,
       appuser13_.full_name                    as col_20_0_
from vehicle_request vehiclereq0_
         cross join app_user appuser1_
         cross join unit unit4_
         cross join app_user appuser11_
         cross join vehicle vehicle5_
         cross join vehicle_request_action vehiclereq6_
         cross join garage garage12_
         cross join app_user appuser13_
where vehiclereq0_.app_user_id = appuser1_.id
  and appuser1_.unit_id = unit4_.id
  and unit4_.director_id = appuser11_.id
  and vehiclereq0_.latest_action_id = vehiclereq6_.id
  and vehiclereq0_.garage = garage12_.id
  and vehiclereq0_.assigned_vehicle_driver_id = appuser13_.id
  and vehiclereq0_.assigned_vehicle_id = vehicle5_.id
  and (coalesce(null, null) is null or appuser1_.unit_id in (null))
  and (coalesce(null, null) is null or vehiclereq0_.garage in (null))
  and (coalesce(null, null) is null or vehiclereq0_.assigned_vehicle_id in (null))
  and (coalesce(null, null) is null or vehiclereq6_.vehicle_request_status in (null))
order by vehiclereq0_.created_at desc

这些语句可以为空,

and vehiclereq0_.assigned_vehicle_driver_id = appuser13_.id
and vehiclereq0_.assigned_vehicle_id = vehicle5_.id
628mspwn

628mspwn1#

简而言之,这就是HQL的工作方式。内连接语义是路径表达式的默认语义。如果需要左连接语义,则必须显式地编写连接。
我认为,这是Blaze-Persistence实体视图的一个完美用例,因为JPA/Hibernate上的这个库默认为路径保留了连接语义。
我创建这个库是为了允许在JPA模型和自定义接口或抽象类定义的模型之间进行简单的Map,就像Spring Data Projections一样。其思想是您可以按照自己喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)Map到实体模型。
使用Blaze-Persistence Entity-Views时,您的用例的DTO模型可能如下所示:

@EntityView(VehicleRequest.class)
public interface GarageVehicleRequestLookupProjection {
    @IdMapping
    Long getId();
    AppUserProjection getAppUser();
    @AttributeFilter(EqualFilter.class)
    Instant getCreatedAt();
    @AttributeFilter(BetweenFilter.class)
    Instant getStartAt();
    Instant getEndAt();
    String getDestination();
    String getReason();
    VehicleProjection getAssignedVehicle();
    VehicleDriverProjection getAssignedVehicleDriver();
    @Mapping("garae.name")
    String getGarageName();
    VehicleType getVehicleTypeRequested();
    RequestType getRequestType();
    String getDescription();
    boolean isDriverRequested();
    @Mapping("latestAction.vehicleRequestStatus")
    VehicleRequestStatus getStatus();

    @EntityView(AppUser.class)
    interface AppUserProjection {
        String getFullName();
        @Mapping("unit.id")
        Long getUnitId();
        @Mapping("unit.name")
        Long getUnitName();
        @Mapping("unit.director.id")
        Long getUnitDirectorId();
        @Mapping("unit.director.fullName")
        Long getUnitDirectorFullName();
    }
    @EntityView(Vehicle.class)
    interface VehicleProjection {
        @IdMapping
        Long getId();
        String getPlate();
    }
    @EntityView(VehicleDriver.class)
    interface VehicleDriverProjection {
        @IdMapping
        Long getId();
        String getFullName();
    }

}

查询是将实体视图应用于查询的问题,最简单的是按id查询。
GarageVehicleRequestLookupProjection a = entityViewManager.find(entityManager, GarageVehicleRequestLookupProjection.class, id);
Spring Data 集成允许您像Spring Data 投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

Page<GarageVehicleRequestLookupProjection> findAll(Pageable pageable);

最好的部分是,它将只获取实际需要的状态,类似于您的自定义查询!
使用Spring Data JPA规范或EntityViewSettingProcessor,您还可以更高效地实现过滤:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features

相关问题