我是Spring启动的新手,我正在编写一个应用程序来练习,使用Postresql作为db。这个应用程序是关于航空的。有4个实体:航空公司、飞机、机场和航班。
我尝试了GET, POST和DELETE请求,所有工作在航空公司,飞机和机场,但我有一个问题,试图添加和更新航班。Flight有4个ManyToOne关系,这是类:
@Entity
@Table
public class Flight {
@Id
@Column(name = "flight_number")
private String flightNumber;
@ManyToOne
@JoinColumn(name = "origin")
private Airport origin;
@ManyToOne
@JoinColumn(name = "destination")
private Airport destination;
@Column(name = "departure_time")
private Timestamp departureTime;
@Column(name = "arrival_time")
private Timestamp arrivalTime;
@ManyToOne
@JoinColumn(name = "airline")
private Airline airline;
@ManyToOne
@JoinColumn(name = "airplane")
private Airplane airplane;
private Time duration;
private int passengers;
...
}
我不明白的是如何插入和更新这个实体,而不需要传递Objects
(Airline
和Airport
),而只是外键(如直接在db上工作时)。
下面是我的代码添加一个新的航班,需要的对象:
public void addFlight(Flight flight) {
boolean exists = flightRepository.existsById(flight.getFlightNumber());
if (exists) {
throw new IllegalStateException("Flight with number" + flight.getFlightNumber()
+ "already exists");
}
flightRepository.save(flight);
}
我还尝试为存储库中的更新编写一个特定的查询,然后调用PUT请求,但我得到一些错误,说我需要传递Airline和Airport,而不是表示这两个实体的id的字符串。
*@Modifying
@Query("update Flight " +
"set origin = :origin, " +
"destination = :destination, " +
"departureTime = :departureTime, " +
"arrivalTime = :arrivalTime, " +
"airline = :airline, " +
"airplane = :airplane, " +
"passengers = :passengers, " +
"duration = :duration " +
"where flightNumber = :flightNumber")
void updateFlight(@Param("flightNumber") String flightNumber,
@Param("origin") String origin,
@Param("destination") String destination,
@Param("departureTime") Timestamp departureTime,
@Param("arrivalTime") Timestamp arrivalTime,
@Param("airline") String airline,
@Param("airplane") Long airplane,
@Param("passengers") Integer passengers,
@Param("duration") Time duration);*/
总结:我想知道是否有一种方法可以避免在创建和更新期间传递代表ManyToOne关系的整个对象。
方法addFlight(Flight flight)
是从你的控制器调用吗?如果是这种情况,你应该从你的控制器获得一个DTO,你将映射到你的服务层中的实体中。
从DTO你需要提供Airline
,Airplane
和Airport
对象的id,这样你就可以从DB中检索它们,并在Flight
实体上设置它们。
我可以提出一个解决方案:
void addFlight (FlightRequest flightRequest) {
boolean exists = flightRepository.existsById(flightRequest.flightNumber());
if (exists) {
throw new IllegalStateException("Flight with number" + flight.getFlightNumber()
+ "already exists");
}
Flight flight = new Flight();
airportRepository.findById(flightRequest.airportOriginId())
.ifPresentOrElse(airportOrigin -> {
flight.setAirportOrigin(airportOrigin);
},
() -> new IllegalStateException(...)
);
airportRepository.findById(flightRequest.airportDestinationId())
.ifPresentOrElse(airportDestination -> {
flight.setAirportDestination(airportDestination);
},
() -> new IllegalStateException(...)
);
airlineRepository.findById(flightRequest.airlineId())
.ifPresentOrElse(airline -> {
flight.setAirline(airline);
},
() -> new IllegalStateException(...)
);
airplaneRepository.findById(flightRequest.airplaneId())
.ifPresentOrElse(airplane -> {
flight.setAirplane(airplane);
},
() -> new IllegalStateException(...)
);
flight.setFlightNumber(flightRequest.flightNumber());
flight.setDepartureTime(flightRequest.departureTime());
flight.setArrivalTime(flightRequest.arrivalTime());
flight.setDuration(flightRequest.duration());
flight.setPassengers(flightRequest.passengers());
...
flightRepository.save(flight);
}
public record FlightRequest(
String flightNumber,
String airportOriginId,
String airportDestinationId,
Timestamp departureTime,
Timestamp arrivalTime,
String airlineId,
String airplaneId,
Time duration,
int passengers,
...
) {
}
更新你的Flight
,你可以这样做:
Flight updateFlight (FlightRequest flightRequest) {
Flight flight = flightRepository.findById(flightRequest.flightNumber())
.orElseThrow(...);
// Same as above without the call for existsById
...
}
如果您想更进一步,从Spring Data JPA的2.7版本开始,您可以使用getReferenceById
方法而不是findById
。在这种情况下,Spring Data JPA不会生成SQL语句来获取实体,在我们的情况下,我们只想设置外键。你可以在这里找到更多信息