如何在默认findById中获取已获取ManyToOne属性的子ManyToOne属性



我有三个由ManyToOne关系相关的实体:

@Entity
@Table(name = "cabin_layout")
@Getter
@Setter
@NoArgsConstructor
@ToString
public class CabinLayout {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@JsonIgnore
@NotNull
@ManyToOne(optional = false)
private MissionProfile missionProfile;
}
@Entity
@Table(name = "mission_profile")
@Getter
@Setter
@NoArgsConstructor
@ToString
public class MissionProfile {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "mission_profile_id")
private Set<FlightRangeShare> flightRangeShares; 
@JsonIgnore
@NotNull
@ManyToOne(optional = false)
private AircraftSeries aircraftSeries;
}
@Entity
@Table(name = "aircraft_series")
@Getter
@Setter
@NoArgsConstructor
@ToString
public class AircraftSeries {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotNull
@Column(unique = true)
@Enumerated(EnumType.STRING)
private AircraftSeriesEnum aircraftSeriesEnum;
}

我正在尝试访问cabinLayout实体的missionProfile属性的aircraftSeries子属性:

public int calcProportionalFuelConsumptionInKg(Long cabinLayoutId, FlightRangeEnum flightRangeEnum) {
return cabinLayoutRepository.findById(cabinLayoutId).map(cabinLayout -> {
missionProfileRepository.findById(cabinLayout.getMissionProfile().getId()).map(missionProfile -> {
int aircraftWeightInKg = missionProfile.getAircraftSeries().getAircraftSeriesEnum()
.getZeroFuelWeightInKg(); // The aircraftWeightInKg is set all right
aircraftWeightInKg = cabinLayout.getMissionProfile().getAircraftSeries().getAircraftSeriesEnum()
.getZeroFuelWeightInKg(); // The getAircraftSeries() returns null
return aircraftWeightInKg;
})

但是,当对cabinLayout实体执行findById时,它只找到其missionProfile属性,并将aircraftSeries子属性留空。

只有在对missionProfile执行额外的伪冗余findById时,才会获取aircraftSeries属性。

提取仅在一个层次上完成,提取实体属性,但不提取实体属性子属性。

cabinLayout变量上的调试器显示:

id: Long@219
missionProfile: MissionProfile@220 "MissionProfile(id=1, aircraftSeries=null)"
aircraftSeries: null

我知道调试器正在代理,并且不会在其视图中触发获取,所以我让它运行测试直到失败。我还可以看到在终端中运行测试时的错误:

2022-10-07 16:30:17,894 ERROR [main] com.exception.ControllerExceptionHandler: Uncatched exception
java.lang.NullPointerException: null
at com.service.EquipmentCalculatorService.calcEquipmentProportionalFuelConsumptionInKg(EquipmentCalculatorService.java:143)

我还尝试了一些明确的加入和渴望的获取,但这丝毫没有改变。

@JsonIgnore
@NotNull
@Fetch(FetchMode.JOIN)
@ManyToOne(optional = false, fetch = FetchType.EAGER)
private MissionProfile missionProfile;
@JsonIgnore
@NotNull
@Fetch(FetchMode.JOIN)
@ManyToOne(optional = false, fetch = FetchType.EAGER)
private AircraftSeries aircraftSeries;

奇怪的是,missionProfile实体实际上有另一个集合属性,可以正常提取:

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true)
@JoinColumn(name = "mission_profile_id")
private Set<FlightRangeShare> flightRangeShares;

我使用的是Java 11 中的spring-boot starter parent 2.7.0

更新:根据grigouille的回复,我创建了存储库:

@Query("SELECT cl FROM CabinLayout cl LEFT JOIN FETCH cl.missionProfile mp LEFT JOIN FETCH mp.aircraftSeries WHERE cl.id = :id")
Optional<CabinLayout> findByIdFetching(@Param("id") Long id);

我称之为:

return cabinLayoutRepository.findByIdFetching(cabinLayoutId).map(cabinLayout -> {

但它仍然给出了完全相同的错误。

日志显示实际的SQL语句:

select cabinlayou0_.id as id1_5_0_, missionpro1_.id as id1_18_1_, aircraftse2_.id as id1_1_2_, cabinlayou0_.description as descript2_5_0_, cabinlayou0_.mission_profile_id as mission_7_5_0_, cabinlayou0_.name as name3_5_0_, cabinlayou0_.tags as tags4_5_0_, cabinlayou0_.user_id as user_id5_5_0_, cabinlayou0_.workflow_status_enum as workflow6_5_0_, missionpro1_.aircraft_series_id as aircraft5_18_1_, missionpro1_.flight_cycles as flight_c2_18_1_, missionpro1_.lifetime as lifetime3_18_1_, missionpro1_.name as name4_18_1_, aircraftse2_.aircraft_series_enum as aircraft2_1_2_ from cabin_layout cabinlayou0_ left outer join mission_profile missionpro1_ on cabinlayou0_.mission_profile_id=missionpro1_.id left outer join aircraft_series aircraftse2_ on missionpro1_.aircraft_series_id=aircraftse2_.id where cabinlayou0_.id=?

更新:在评论之后,我还尝试添加属性:

spring:
jpa:
hibernate:
max_fetch_depth: 3
hibernate:
max_fetch_depth: 3

但它什么也没改变。

更新:我尝试了一个投影的变通方法。

@Query("SELECT new com.domain.projection.CabinLayoutView(cl, mp, ars, ce, e) FROM CabinEquipment ce JOIN ce.equipment e JOIN ce.cabinLayout cl JOIN cl.missionProfile mp JOIN mp.aircraftSeries ars WHERE cl.id = :id")
List<CabinLayoutView> findByIdFetching(@Param("id") Long id);

然后我可以重建我的实体:

public CabinLayout findByIdFetching(Long cabinLayoutId) {
return toCabinLayout(cabinLayoutRepository.findByIdFetching(cabinLayoutId));
}
private CabinLayout toCabinLayout(List<CabinLayoutView> cabinLayoutViews) {
if (!cabinLayoutViews.isEmpty()) {
CabinLayout cabinLayout = null;
Set<CabinEquipment> cabinEquipments = new HashSet<>();
for (CabinLayoutView cabinLayoutView : cabinLayoutViews) {
cabinLayout = cabinLayoutView.getCabinLayout();
cabinLayout.setMissionProfile(cabinLayoutView.getMissionProfile());
cabinLayout.getMissionProfile().setAircraftSeries(cabinLayoutView.getAircraftSeries());
CabinEquipment cabinEquipment = cabinLayoutView.getCabinEquipment();
cabinEquipment.setEquipment(cabinLayoutView.getEquipment());
cabinEquipments.add(cabinEquipment);
}
if (Objects.nonNull(cabinLayout)) {
cabinLayout.getCabinEquipments().clear();
cabinLayout.getCabinEquipments().addAll(cabinEquipments);
}
return cabinLayout;
} else {
throw new NotFoundException("The cabin layout projection was empty");
}
}

尝试在一个查询中获取所有属性:

EntityManager em;
Long id;
CabinLayout cabinLayout = em.createQuery("select c from CabinLayout c left join fetch c.missionProfile m left join fetch m.aircraftSeries where c.id = :id", CabinLayout.class)
.setParameter("id", id)
.getSingleResult();
//load flightRangeShares if you want
cabinLayout.getMissionProfile().getFlightRangeShares().size();
return cabinLayout

最新更新