Spring 数据 Jpa + Spring 使用@Query 的投影(本机和 JPQL)returns 相关实体为 null
Spring Data Jpa + Spring Projections using @Query (native and JPQL) returns null for related entities
我想要实现的是使用 JpaRepository 接口在 Spring Data Jpa 中使用 3 种不同的方法编写相同的查询:
- 命名方法策略。
- @使用 JPQL 查询。
- @查询原生SQL.
在这里您可以看到我是如何创建具有所有关系的访问实体的,我正在尝试 select。
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 我不会复制 class 上方的所有注释。
所以这是实验。我创建的方法应该 return 在给定时间间隔内对特定医生的所有访问。
这是我写的方法:
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 Class 获取 doctor firstName 和 lastName -> 使用 getter 并内置在另一个 Doctor 接口中以从 Entity 访问字段。在这里你可以看到 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
}
]
现在我想使用带有 JPQL 和本机的 @Query 来获得相同的结果 SQL。因此,我打印了从此方法生成的 SQL 我尝试将其与 @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);
@查询+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);
这两个查询 return 具有 getter 的 JSONs 或具有空值形式的 Doctor 接口 VisitView :
[
{
"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,因为我阅读了很多关于不同版本中出现的bug。尝试按字母顺序对 selected 字段进行分组,因为我在此处的另一个问题中找到了此提示。尝试按照建议使用 @Join Column 注释,但它也没有帮助。
现在我快疯了,因为我不知道为什么它不起作用。
有人可以帮我吗?
Hibernate 核心版本 -> 5.4。14.Final
Hibernate orm-search 版本 -> 5.11.5.Final
编辑:
上面的问题已经解决但是..
我还有一个关于这个话题的问题。
Entity 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();
}
}
此接口return 只有一个对象,其中包含使用命名方法策略的医疗服务列表。这是来自 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 界面及其工作中被注释掉了,但它 returning 不是 1 Visit Object with List of medical services,而是 2 个相同的对象,每个人都有一项医疗服务。看起来像这样:
{
"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 响应吗??
您正在使用 d.firstName as firstName
和 d.lastName as lastName
这意味着您要在界面中投影 firstName
和 lastName
字段中的值。
在@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);
我想要实现的是使用 JpaRepository 接口在 Spring Data Jpa 中使用 3 种不同的方法编写相同的查询:
- 命名方法策略。
- @使用 JPQL 查询。
- @查询原生SQL.
在这里您可以看到我是如何创建具有所有关系的访问实体的,我正在尝试 select。
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 我不会复制 class 上方的所有注释。 所以这是实验。我创建的方法应该 return 在给定时间间隔内对特定医生的所有访问。
这是我写的方法:
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 Class 获取 doctor firstName 和 lastName -> 使用 getter 并内置在另一个 Doctor 接口中以从 Entity 访问字段。在这里你可以看到 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
}
]
现在我想使用带有 JPQL 和本机的 @Query 来获得相同的结果 SQL。因此,我打印了从此方法生成的 SQL 我尝试将其与 @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);
@查询+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);
这两个查询 return 具有 getter 的 JSONs 或具有空值形式的 Doctor 接口 VisitView :
[
{
"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,因为我阅读了很多关于不同版本中出现的bug。尝试按字母顺序对 selected 字段进行分组,因为我在此处的另一个问题中找到了此提示。尝试按照建议使用 @Join Column 注释,但它也没有帮助。
现在我快疯了,因为我不知道为什么它不起作用。 有人可以帮我吗?
Hibernate 核心版本 -> 5.4。14.Final
Hibernate orm-search 版本 -> 5.11.5.Final
编辑: 上面的问题已经解决但是.. 我还有一个关于这个话题的问题。
Entity 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();
}
}
此接口return 只有一个对象,其中包含使用命名方法策略的医疗服务列表。这是来自 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 界面及其工作中被注释掉了,但它 returning 不是 1 Visit Object with List of medical services,而是 2 个相同的对象,每个人都有一项医疗服务。看起来像这样:
{
"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 响应吗??
您正在使用 d.firstName as firstName
和 d.lastName as lastName
这意味着您要在界面中投影 firstName
和 lastName
字段中的值。
在@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);