Spring Data JPA + Spring Projections使用@Query(本地和JPQL)返回相关实体为空

3

我想做的是使用Spring Data Jpa中的JpaRepository接口,用3种不同的方法编写相同的查询:

  1. 命名方法策略。
  2. @Query使用JPQL。
  3. @Query本地SQL。

在这里,您可以看到我创建了Visit Entity,并选择了所有相关关系。

public class Visit {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    long visitId;
    LocalDateTime dateFrom;
    LocalDateTime dateTo;
    @Enumerated(EnumType.STRING)
    VisitStatus status;
    @ManyToOne(fetch = FetchType.EAGER)
    @JsonManagedReference
    Doctor doctor;
    @ManyToOne
    @JsonManagedReference
    Patient patient;
    @ManyToMany
    @JsonManagedReference
    List<Disease> diseases;
    @ManyToMany
    @JsonManagedReference
    List<MedicalService> medicalServices;
    String mainSymptoms;
    String treatment;
    String allergy;
    String addiction;
    String comment;

我正在使用Project Lombok,以便我不需要复制类上方的所有注释。 所以这里是实验。我创建了一个方法,应该返回在给定时间间隔内特定医生的所有就诊记录。
这是我编写的方法:
List<VisitView> findByDoctorIdAndStatusAndDateFromGreaterThanEqualAndDateToLessThanEqual
            (long doctorId, VisitStatus visitStatus, LocalDateTime dateFrom, LocalDateTime dateTo);

正如您所看到的,我已经使用Spring Projections实现了VisitView接口。

这就是它:

public interface VisitView {
    long getDoctorId();
//    Doctor getDoctor();
//    interface Doctor {
//        String getFirstName();
//        String getLastName();
//    }
    String getDoctorFirstName();
    String getDoctorLastName();
    Long getPatientId();
    long getVisitId();
    LocalDateTime getDateFrom();
    LocalDateTime getDateTo();
    VisitStatus getStatus();
}

使用这种方法,一切正常。我可以通过 Doctor Entity 类的 getter 方法和内置的 Doctor 接口两种方式获取医生 firstName 和 lastName 字段。下面是使用项目接口的两个 JSON 示例:

[
    {
        "status": "PAID",
        "visitId": 395,
        "dateTo": "2019-04-10T08:30:00",
        "dateFrom": "2019-04-10T08:00:00",
        "doctorId": 401,
        "patientId": 394,
        "doctorFirstName": "Aleksander",
        "doctorLastName": "Ziółko"
    }
]

[
    {
        "status": "PAID",
        "visitId": 395,
        "dateTo": "2019-04-10T08:30:00",
        "doctor": {
            "firstName": "Aleksander",
            "lastName": "Ziółko"
        },
        "dateFrom": "2019-04-10T08:00:00",
        "doctorId": 401,
        "patientId": 394
    }
]

现在我想使用@Query和JPQL以及本地SQL实现相同的结果。因此,我打印出了这个方法生成的SQL,并尝试将其与@Query注释一起使用。下面是它的代码:

@Query + 本地SQL:

@Query(value = "SELECT d.id as doctorId, d.firstName as firstName, d.lastName as lastName, p.id as patientId, v.id as visitId, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status \n" +
            "FROM visit v \n" +
            "LEFT OUTER JOIN doctor d on v.doctor_id=d.id \n" +
            "LEFT OUTER JOIN users ud on d.id=ud.id \n" +
            "LEFT OUTER JOIN patient p on v.patient_id=p.id \n" +
            "LEFT OUTER JOIN users up on p.id=up.id \n" +
            "where d.id= :doctorId and v.status= :status and v.dateFrom>= :dateFrom and v.dateTo<= :dateTo ", nativeQuery = true)
    List<VisitView> searchForDoctorsVisitByStatusAndTimeIntervalNativeQuery(
            @Param("doctorId") long doctorId, @Param("status") String status, @Param("dateFrom") LocalDateTime dateFrom, @Param("dateTo") LocalDateTime dateTo);

@Query + JPQL:

@Query("SELECT d.id as doctorId, d.firstName as firstName, d.lastName as lastName, p.id as patientId, v.visitId as visitId, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status \n" +
            "FROM Visit v \n" +
            "LEFT OUTER JOIN Doctor d ON v.doctor.id=d.id \n" +
            "LEFT OUTER JOIN Patient p ON v.patient.id=p.id \n" +
            "WHERE d.id= :doctorId AND v.status= :status AND v.dateFrom>= :dateFrom AND v.dateTo<= :dateTo")
    List<VisitView> searchForDoctorsVisitByStatusAndTimeIntervalJqplQuery(
            @Param("doctorId") long doctorId, @Param("status") VisitStatus status, @Param("dateFrom") LocalDateTime dateFrom, @Param("dateTo") LocalDateTime dateTo);

这两个查询从VisitView返回带有null值的getter或Doctor接口的JSON:
[
    {
        "status": "PAID",
        "visitId": 395,
        "dateTo": "2019-04-10T08:30:00",
        "dateFrom": "2019-04-10T08:00:00",
        "doctorId": 401,
        "patientId": 394,
        "doctorFirstName": null,
        "doctorLastName": null
    }
]

[
    {
        "status": "PAID",
        "visitId": 395,
        "dateTo": "2019-04-10T08:30:00",
        "doctor": null,
        "dateFrom": "2019-04-10T08:00:00",
        "doctorId": 401,
        "patientId": 394
    }
]

我已经尝试了许多版本的Hibernate,因为我读到很多不同版本出现的错误。我试图按照字母顺序对选定字段进行分组,因为我在另一个问题中发现了这个提示。我尝试使用@JoinColumn注释,因为建议这样做,但也没有帮助。

现在我变得疯狂,因为我无法弄清楚为什么它不起作用。 有人能帮帮我吗?

Hibernate核心版本 -> 5.4.14.Final

Hibernate ORM搜索版本 -> 5.11.5.Final

编辑: 上面的问题已经解决,但是.. 我有另一个关于这个主题的问题。

实体Visit与MedicalServices处于@ManyToMany关系。现在我想拉取这个列表,所以我投影了另一个接口:

public interface VisitInfoWithPatientAndMedServices {
    LocalDateTime getDateFrom();
    LocalDateTime getDateTo();
    VisitStatus getStatus();
//  long getMedicalServicesId();
//  String getMedicalServicesService();
//  float getMedicalServicesPrice();
    List<MedicalService> getMedicalServices();
    interface MedicalService {
        String getId();
        String getService();
        float getPrice();
    }
}

这个接口使用命名方法策略,只返回一个包含医疗服务列表的对象。以下是Postman返回的JSON格式数据:
[
    {
        "status": "PAID",
        "medicalServices": [
            {
                "id": "3",
                "service": "Something",
                "price": 250.0
            },
            {
                "id": "4",
                "service": "USG",
                "price": 400.0
            }
        ],
        "dateTo": "2019-04-10T08:30:00",
        "dateFrom": "2019-04-10T08:00:00"
    }
]

但是我仍然不能正确使用原生SQL和@Query注释。我知道可以使用此问题的解决方案来获取它,您可以在上述VisitInfoWithPatientAndMedServices接口中看到已注释掉它,并且它可以工作,但它返回的不是一个带有医疗服务列表的访问对象,而是两个相同的对象,每个对象都有一个医疗服务。它看起来像这样:

    {
        "dateTo": "2019-04-10T08:30:00",
        "dateFrom": "2019-04-10T08:00:00",
        "medicalServicesId": 3,
        "medicalServicesPrice": 250.0,
        "medicalServicesService": "Something",
        "status": "PAID"
    },
    {
        "dateTo": "2019-04-10T08:30:00",
        "dateFrom": "2019-04-10T08:00:00",
        "medicalServicesId": 4,
        "medicalServicesPrice": 400.0,
        "medicalServicesService": "USG",
        "status": "PAID"
    }
]

它的工作原理就像在Workbench中一样,因为我正在使用MySQL。

我能否采用命名方法策略和@Query注释(本地SQL和JPQL)来获得相同的JSON响应?

1个回答

7

你正在使用 d.firstName as firstNamed.lastName as lastName 这意味着你想要在接口中投影出 firstNamelastName 字段的值。

在 @Query 中使用 d.firstName as doctorFirstName, d.lastName as doctorLastName 来获取这个值。

 @Query("SELECT d.id as doctorId, d.firstName as doctorFirstName, d.lastName as doctorLastName, p.id as patientId, v.visitId as visitId, v.dateFrom as dateFrom, v.dateTo as dateTo, v.status as status \n" +
            "FROM Visit v \n" +
            "LEFT OUTER JOIN Doctor d ON v.doctor.id=d.id \n" +
            "LEFT OUTER JOIN Patient p ON v.patient.id=p.id \n" +
            "WHERE d.id= :doctorId AND v.status= :status AND v.dateFrom>= :dateFrom AND v.dateTo<= :dateTo")
    List<VisitView> searchForDoctorsVisitByStatusAndTimeIntervalJqplQuery(
            @Param("doctorId") long doctorId, @Param("status") VisitStatus status, @Param("dateFrom") LocalDateTime dateFrom, @Param("dateTo") LocalDateTime dateTo);

非常感谢您的帮助。是的,我尝试过了,它运行良好。 - Antyfan
当然!我也投了票,但我的声望太小了,无法使其可见。再次感谢你。 - Antyfan
我已经编辑了问题,你能再看一遍吗? - Antyfan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接