在餐厅场景中同时处理预订请求(Handling simultaneous reservation requests in restaurant scenario)

我正在使用Spring数据JPA开发Spring引导和HSQLDB的餐馆预订系统的REST API项目。 该场景是用户可以呼叫预订服务在特定时间预订表格。 在给定的时间段内只能有一次预订。

我的问题是,我如何处理REST API由两个或多个不同用户同时调用以同时预订同一个表的方案。

我搜索了这一点,发现乐观锁定可以用于此,但我不知道它将如何帮助在这种情况下,除非我做任何更新餐厅表记录。

以下是我的项目结构。

预订DAO

@Entity public class Booking { @Id @GeneratedValue private Long id; private String bookingId; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm") private LocalDateTime bookingStart; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm") private LocalDateTime bookingEnd; @OneToOne private RestaurantTable table; private String customerName; ..Setter/Getter }

餐厅DAO

@Entity public class RestaurantTable { @Id @GeneratedValue private Long id; private Integer tableId; private Integer capacity; private String description; //Setter/Getter }

预订创建服务。

@Service public class BookingService{ @Transactional public Booking addBooking(final BookingDTO bookingRequest) { LOG.debug("Creating new booking for request: {}", bookingRequest); validateBookingData(bookingRequest); return bookingCreateService.createBooking(bookingRequest); } } @Service public class BookingCreateService { @Autowired private BookingRepository bookingRepository; @Autowired private TableRepository tableRepository; public Booking createBooking(final BookingDTO bookingRequest) { Booking booking = null; final LocalDateTime bookingStart = bookingRequest.getBookingTime(); final LocalDateTime bookingEnd = bookingRequest.getBookingTime().plusHours(1); RestaurantTable table = tableRepository .findAvailableTableWithAdequateSeatingCapacity(bookingStart, bookingEnd, bookingRequest.getCustomers()) .stream().findFirst().orElse(null); if (table != null) { booking = saveBooking(table, bookingStart, bookingEnd, bookingRequest.getCustomerName()); } else { throwErrorWithRecomemdedTime(bookingRequest.getCustomers(), bookingStart); } return booking; } private Booking saveBooking(final RestaurantTable table, final LocalDateTime bookingStart, final LocalDateTime bookingEnd, final String customerName) { Booking booking = new Booking(); booking.setBookingStart(bookingStart); booking.setBookingEnd(bookingEnd); booking.setTable(table); booking.setBookingId(RandomStringUtils.randomAlphanumeric(7)); booking.setCustomerName(customerName); return bookingRepository.save(booking); } private void throwErrorWithRecomemdedTime(final Integer customerCount, final LocalDateTime bookingStart) { Booking closestBookingWithFreeSlot = bookingRepository.findClosestBookingToInputTime(bookingStart); StringBuilder errorMsg = new StringBuilder(); errorMsg.append("Table not available for capacity: ").append(customerCount).append("."); if (closestBookingWithFreeSlot != null) { errorMsg.append(" Table available after " + closestBookingWithFreeSlot.getBookingEnd() + "."); } throw new DataValidationException(errorMsg.toString()); } }

如果我使用Thread.sleep完成BookingService完成并创建另一个预订同一个表的请求,它将为同一个表创建两个同时预订。 我怎样才能避免这种情况? 我应该让这个同步吗? 同步原因性能问题?

I am working on a REST API project for a restaurant reservation system with Spring boot and HSQLDB using Spring data JPA. The scenario is users can call booking service to book a table at a particular time. Only one booking must be allowed at a given time slot.

My question is how do i handle the scenario where the REST API is called simultaneously by 2 or more different users to book the same table at the same time.

I searched through this and found optimistic locking can be used for this but i am not sure how it would help in the scenario unless I make any updates to Restaurant Table record.

Below is my project structure.

Booking DAO

@Entity public class Booking { @Id @GeneratedValue private Long id; private String bookingId; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm") private LocalDateTime bookingStart; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm") private LocalDateTime bookingEnd; @OneToOne private RestaurantTable table; private String customerName; ..Setter/Getter }

Restaurant DAO

@Entity public class RestaurantTable { @Id @GeneratedValue private Long id; private Integer tableId; private Integer capacity; private String description; //Setter/Getter }

Booking Create service.

@Service public class BookingService{ @Transactional public Booking addBooking(final BookingDTO bookingRequest) { LOG.debug("Creating new booking for request: {}", bookingRequest); validateBookingData(bookingRequest); return bookingCreateService.createBooking(bookingRequest); } } @Service public class BookingCreateService { @Autowired private BookingRepository bookingRepository; @Autowired private TableRepository tableRepository; public Booking createBooking(final BookingDTO bookingRequest) { Booking booking = null; final LocalDateTime bookingStart = bookingRequest.getBookingTime(); final LocalDateTime bookingEnd = bookingRequest.getBookingTime().plusHours(1); RestaurantTable table = tableRepository .findAvailableTableWithAdequateSeatingCapacity(bookingStart, bookingEnd, bookingRequest.getCustomers()) .stream().findFirst().orElse(null); if (table != null) { booking = saveBooking(table, bookingStart, bookingEnd, bookingRequest.getCustomerName()); } else { throwErrorWithRecomemdedTime(bookingRequest.getCustomers(), bookingStart); } return booking; } private Booking saveBooking(final RestaurantTable table, final LocalDateTime bookingStart, final LocalDateTime bookingEnd, final String customerName) { Booking booking = new Booking(); booking.setBookingStart(bookingStart); booking.setBookingEnd(bookingEnd); booking.setTable(table); booking.setBookingId(RandomStringUtils.randomAlphanumeric(7)); booking.setCustomerName(customerName); return bookingRepository.save(booking); } private void throwErrorWithRecomemdedTime(final Integer customerCount, final LocalDateTime bookingStart) { Booking closestBookingWithFreeSlot = bookingRepository.findClosestBookingToInputTime(bookingStart); StringBuilder errorMsg = new StringBuilder(); errorMsg.append("Table not available for capacity: ").append(customerCount).append("."); if (closestBookingWithFreeSlot != null) { errorMsg.append(" Table available after " + closestBookingWithFreeSlot.getBookingEnd() + "."); } throw new DataValidationException(errorMsg.toString()); } }

If i hold the BookingService from completion using Thread.sleep and create another requets for booking same table, it would create two bookings for same table with same time. How can I avoid this? Should I make this synchronized? Will synchronised cause performance issue?

最满意答案

如果您在包含日期,表格和时间段的数据库表格上添加一个唯一约束条件,则只要您使用的是事务处理,就应该在两者之一上发生错误。 您可以捕获该错误并向客户端返回一条消息,表明时间不再可用。

If you add a unique constraint on the database table which includes the day, table and time slot you should get an error on one of the two as long as you are using transactions. You can catch the error and return a message to the client that the time is no longer available.

更多推荐