javajpaswaggerjava-ee-7

JPA @ManyToOne Only having the ID in the JSON swagger body?


I have created a JPA object with a @ManyToOne relationship with 2 other objects. But I'm having problems getting it to work. Especially regarding the JSON body. I have removed some annotations if they didn't seem important

public class Booking implements Serializable {

    @Id
    @ApiModelProperty(readOnly = true)
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.DETACH)
    @JoinColumn(name = "customer_id")
    private Customer customer;

    @NotNull
    @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.DETACH)
    @JoinColumn(name = "taxi_id")
    private Taxi taxi;

    @NotNull
    @Future(message = "Bookings can not be in the past. Please choose one from the future")
    @Column(name = "booking_date")
    @Temporal(TemporalType.DATE)
    private Date bookingDate;
}
public class Customer implements Serializable {
    @Id
    @ApiModelProperty(readOnly = true)
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;
    private String firstName;
    private String lastName;
    private String email;
    private String phoneNumber;
    @OneToMany(mappedBy = "taxi", cascade = CascadeType.ALL)
    @ApiModelProperty(hidden = true)
    private List<Booking> bookings;
}
public class Taxi implements Serializable {
    @Id
    @ApiModelProperty(readOnly = true)
    @GeneratedValue(strategy = GenerationType.TABLE)
    private Long id;
    private String registration;
    private String numberOfSeats;
    @OneToMany(mappedBy = "taxi", cascade = CascadeType.ALL)
    @ApiModelProperty(hidden = true)
    private List<Booking> bookings;
}

The RestService

public Response createBooking(
            @ApiParam(value = "Customer ID, Taxi ID and a booking date required to create a booking", required = true)
                    org.jboss.quickstarts.wfk.booking.Booking booking)
    {
        if (booking == null) {
            throw new RestServiceException("Bad Request", Response.Status.BAD_REQUEST);
        }

        Response.ResponseBuilder builder;

        try {
            // Go add the new Booking.
            service.create(booking);
            builder = Response.status(Response.Status.CREATED).entity(booking);


        } catch (ConstraintViolationException ce) {
            //Handle bean validation issues
            Map<String, String> responseObj = new HashMap<>();

            for (ConstraintViolation<?> violation : ce.getConstraintViolations()) {
                responseObj.put(violation.getPropertyPath().toString(), violation.getMessage());
            }
            throw new RestServiceException("Bad Request", responseObj, Response.Status.BAD_REQUEST, ce);

        } catch (Exception e) {
            // Handle generic exceptions
            throw new RestServiceException(e);
        }

        log.info("createBooking completed. Booking = " + booking.toString());
        return builder.build();
    }

The service class where crud is the repository

@Inject
private BookingRepository crud;

Booking create(Booking booking) throws ConstraintViolationException, ValidationException, Exception {
        log.info("BookingService Testing: " + booking.toString());
        log.info("BookingService.create() - Creating " + booking.getCustomer().getId() + " " + booking.getTaxi().getId());
        
        // Check to make sure the data fits with the parameters in the Booking model and passes validation.
        //validator.validateBooking(booking);

        // Write the booking to the database.
        return crud.create(booking);
    }

The repository class

@Inject
private EntityManager em;
Booking create(Booking booking) throws ConstraintViolationException, ValidationException, Exception {
        log.info("BookingRepository.create() - Creating " + booking.getCustomer().getId() + " " + booking.getTaxi().getId());

        // Write the booking to the database.
        em.persist(booking);

        return booking;
    }

But when I look at my swagger documentation it shows this. But I don't want to create new objects. I want to access the objects with the id

{
  "customer": {
    "firstName": "string",
    "lastName": "string",
    "email": "string",
    "phoneNumber": "string"
  },
  "taxi": {
    "registration": "string",
    "numberOfSeats": 0
  },
  "bookingDate": "2021-11-11T22:20:34.952Z"
}

So I would like to send this as body

{
  "customer_id": 0,
  "taxi_id": 0,
  "bookingDate": "2021-11-11T22:20:34.952Z"
}

Does anybody have any solutions to this? Let me know if you need more code.


Solution

  • You need to change your RestService to accept an instance of the following class instead of Booking:

    public class BookingCreationRequest {
        @JsonProperty("customer_id")
        private Long customerId;
        
        @JsonProperty("taxi_id")
        private Long taxiId;
        
        private Date bookingDate;
    }
    

    So something like the following in your RestService:

    public Response createBooking(BookingCreationRequest bookingCreationRequest) {
         (...)
         
         service.create(bookingCreationRequest);
    
         (...)
    }
    

    Now in your service class, you need to get both the Customer and the Taxi entities associated with the request, build the Booking object and persist it in the database:

    @Inject
    private BookingRepository crud;
    
    @Inject
    private TaxiService taxiService;
    
    @Inject
    private CustomerService customerService;
    
    Booking create(BookingCreationRequest bookingCreationRequest) throws ConstraintViolationException, ValidationException, Exception {
        (...) 
    
        Taxi taxi = taxiService.getTaxiById(bookingCreationRequest.getTaxiId());
        Customer customer = customerService.getCustomerById(bookingCreationRequest.getCustomerId());
        Booking booking = new Booking();
        booking.setTaxi(taxi);
        booking.setCustomer(customer);
        booking.setBookingDate(bookingCreationRequest.getBookingDate());
    
        return crud.create(booking);
    }