简体   繁体   中英

How to update entity with OneToMany relationship in Spring Boot

I'm new to Spring boot and I'm writing an application to practice, with Postresql as db. The application is about aviation. There are 4 entity: Airline, Airplane, Airport and Flight.

I tried GET, POST and DELETE requests and all works on Airline, Airplane and Airport, but I have a problem trying to add and update Flight. Flight has 4 ManyToOne relationships, this is the class:

@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;
    ...
}

What I don't understand is how to insert and update this entity without the need to pass the Objects ( Airline and Airport ) but only the foreign keys (as when working directly on db).

Here is my code for add a new flight that requires the objects:

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);
}

I also tried writing a specific query for the update in the repository and then call on PUT request, but I get some errors saying that I need to pass Airline and Airport, not strings that represents the IDs of these 2 entities.

*@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);*/

To summarize: I want to know if there is a method to avoid passing the entire Object that represents the ManyToOne relationship during creation and update.

Is the method addFlight(Flight flight) called from your controller? If that's the case, you should get a DTO from your controller that you'll map in an entity in your service layer.

From the DTO you'll need to provide the ids of Airline , Airplane and Airport objects so that you can retrieve them from the DB and set them on the Flight entity.

Here is a solution I can suggest:

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,
        ...
) {  
}

And for updating your Flight , you could do it like this:

Flight updateFlight (FlightRequest flightRequest) {
    Flight flight = flightRepository.findById(flightRequest.flightNumber())
                        .orElseThrow(...);
                        
    // Same as above without the call for existsById
    ...
}

If you want to go further, since version 2.7 of Spring Data JPA, you can use getReferenceById method instead of findById . In that case Spring Data JPA won't generate an SQL statement to get the entity, in our case we only want to set the foreign keys. You can find more about this here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM