I am building a web application based on hotel room booking and testing my REST API through postman and this @Future annotation from import jakarta.validation.constraints.Future; is giving me 500 response, while I tried to hit the link mentioned in the screenshot, I tried again by removing this @Future
annotation and it is working perfectly, so please tell me if there is any alternative or solution for this issue.
I have used this @Future
over one attribute(checkOutDate) of entity, below are the codes related to Booking: -
Booking.java
package com.Yash.Astoria.entities;import jakarta.validation.constraints.Future;import jakarta.persistence.*;import jakarta.validation.constraints.Min;import jakarta.validation.constraints.NotNull;import lombok.Data;import java.time.LocalDate;@Data@Entity@Table(name = "bookings")public class Booking { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @NotNull(message = "CheckIn Date is required") private LocalDate checkInDate; @Future(message = "check out date must be in the future") private LocalDate checkOutDate; @Min(value = 1, message = "Atleast 1 adult should be selected") private int numOfAdults; @Min(value = 0, message = "Number of Childrens should not be less then 0") private int numOfChildren; private int totalNumOfGuests; private String bookingConfirmationCode; @ManyToOne(fetch = FetchType.EAGER) @JoinColumn(name = "user_id") private User user; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "room_id") private Room room; public void getTotalNumberOfGuests(){ this.totalNumOfGuests = this.numOfAdults + this.numOfChildren; } public void setNumOfAdults(int numOfAdults) { this.numOfAdults = numOfAdults; getTotalNumberOfGuests(); } public void setNumOfChildren(int numOfChildren) { this.numOfChildren = numOfChildren; getTotalNumberOfGuests(); } @Override public String toString() { return "Booking{" +"id=" + id +", checkInDate=" + checkInDate +", checkOutDate=" + checkOutDate +", numOfAdults=" + numOfAdults +", numOfChildren=" + numOfChildren +", totalNumOfGuests=" + totalNumOfGuests +", bookingConfirmationCode='" + bookingConfirmationCode +'\''+'}'; }}
BookingController.java
package com.Yash.Astoria.controllers;import com.Yash.Astoria.dto.Response;import com.Yash.Astoria.entities.Booking;import com.Yash.Astoria.services.Interface.IBookingService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.ResponseEntity;import org.springframework.security.access.prepost.PreAuthorize;import org.springframework.web.bind.annotation.*;@RestController@RequestMapping("/bookings")public class BookingController { @Autowired private IBookingService bookingService; @PostMapping("/book-room/{roomId}/{userId}") @PreAuthorize("hasAuthority('ADMIN') or hasAuthority('USER')") public ResponseEntity<Response> saveBookings(@PathVariable Long roomId, @PathVariable Long userId, @RequestBody Booking bookingRequest){ Response response = bookingService.saveBooking(roomId, userId, bookingRequest); return ResponseEntity.status(response.getStatusCode()).body(response); } @GetMapping("/all") @PreAuthorize("hasAuthority('ADMIN')") public ResponseEntity<Response> getAllBookings(){ Response response = bookingService.getAllBookings(); return ResponseEntity.status(response.getStatusCode()).body(response); } @GetMapping("/get-by-confirmation-code/{confirmationCode}") public ResponseEntity<Response> getBookingByConfirmationCode(@PathVariable String confirmationCode){ Response response = bookingService.findBookingByConfirmationCode(confirmationCode); return ResponseEntity.status(response.getStatusCode()).body(response); } @DeleteMapping @PreAuthorize("hasAuthority('ADMIN') or hasAuthority('USER')") public ResponseEntity<Response> cancelBooking(@PathVariable Long bookingId){ Response response = bookingService.cancelBooking(bookingId); return ResponseEntity.status(response.getStatusCode()).body(response); }}
IBookingService.java
package com.Yash.Astoria.services.Interface;import com.Yash.Astoria.dto.Response;import com.Yash.Astoria.entities.Booking;public interface IBookingService { Response saveBooking(Long roomId, Long userId, Booking bookingRequest); Response findBookingByConfirmationCode(String confirmationCode); Response getAllBookings(); Response cancelBooking(Long bookingId);}
BookingService.java
package com.Yash.Astoria.services.impl;import com.Yash.Astoria.dto.BookingDTO;import com.Yash.Astoria.dto.Response;import com.Yash.Astoria.entities.Booking;import com.Yash.Astoria.entities.Room;import com.Yash.Astoria.entities.User;import com.Yash.Astoria.exception.OurException;import com.Yash.Astoria.repository.BookingRepository;import com.Yash.Astoria.repository.RoomRepository;import com.Yash.Astoria.repository.UserRepository;import com.Yash.Astoria.services.Interface.IBookingService;import com.Yash.Astoria.services.Interface.IRoomService;import com.Yash.Astoria.utils.Utils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.domain.Sort;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class BookingService implements IBookingService { @Autowired private BookingRepository bookingRepository; @Autowired private IRoomService roomService; @Autowired private RoomRepository roomRepository; @Autowired private UserRepository userRepository; @Override public Response saveBooking(Long roomId, Long userId, Booking bookingRequest) { Response response = new Response(); try { if (bookingRequest.getCheckOutDate().isBefore(bookingRequest.getCheckInDate())) { throw new IllegalArgumentException("Check out date must come after Check in date"); } Room room = roomRepository.findById(roomId).orElseThrow(() -> new OurException("Room Not Found")); User user = userRepository.findById(userId).orElseThrow(() -> new OurException("User Not Found")); List<Booking> existingBookings = room.getBookings(); if (!roomIsAvailable(bookingRequest, existingBookings)) { throw new OurException("Room not Available for selected date range"); } bookingRequest.setRoom(room); bookingRequest.setUser(user); String bookingConfirmationCode = Utils.generateRandomConfirmationCode(10); bookingRequest.setBookingConfirmationCode(bookingConfirmationCode); bookingRepository.save(bookingRequest); response.setStatusCode(200); response.setMessage("successful"); response.setBookingConfirmationCode(bookingConfirmationCode); } catch (OurException e) { response.setStatusCode(404); response.setMessage(e.getMessage()); } catch (Exception e){ response.setStatusCode(500); response.setMessage("Error while Saving a Booking"+e.getMessage()); } return response; } @Override public Response findBookingByConfirmationCode(String confirmationCode) { Response response = new Response(); try { Booking booking = bookingRepository.findBookingByBookingConfirmationCode(confirmationCode).orElseThrow(()-> new OurException("Booking Not Found")); BookingDTO bookingDTO = Utils.mapBookingEntityToBookingDTOPlusBookedRooms(booking, true); response.setStatusCode(200); response.setMessage("successful"); response.setBooking(bookingDTO); } catch (OurException e) { response.setStatusCode(404); response.setMessage(e.getMessage()); } catch (Exception e){ response.setStatusCode(500); response.setMessage("Error Finding a Booking"+e.getMessage()); } return response; } @Override public Response getAllBookings() { Response response = new Response(); try { List<Booking> bookingList = bookingRepository.findAll(Sort.by(Sort.Direction.DESC, "id")); List<BookingDTO> bookingDTOList = Utils.mapBookingListEntityToBookingListDTO(bookingList); response.setStatusCode(200); response.setMessage("successful"); response.setBookingList(bookingDTOList); } catch (OurException e) { response.setStatusCode(404); response.setMessage(e.getMessage()); } catch (Exception e){ response.setStatusCode(500); response.setMessage("Error while retrieving all the Bookings"+e.getMessage()); } return response; } @Override public Response cancelBooking(Long bookingId) { Response response = new Response(); try { bookingRepository.findById(bookingId).orElseThrow(()-> new OurException("Booking Does Not Exist")); bookingRepository.deleteById(bookingId); response.setStatusCode(200); response.setMessage("successful"); } catch (OurException e) { response.setStatusCode(404); response.setMessage(e.getMessage()); } catch (Exception e){ response.setStatusCode(500); response.setMessage("Error Cancelling a Booking"+e.getMessage()); } return response; } // Method to check if a room is available for a given booking request private boolean roomIsAvailable(Booking bookingRequest, List<Booking> existingBookings) { // Stream through the list of existing bookings return existingBookings.stream() // Check if none of the existing bookings match the given conditions .noneMatch(existingBooking -> // Condition 1: Check if the check-in date of the booking request is the same as any existing booking's check-in date bookingRequest.getCheckInDate().equals(existingBooking.getCheckInDate()) // Condition 2: Check if the check-out date of the booking request is before any existing booking's check-out date || bookingRequest.getCheckOutDate().isBefore(existingBooking.getCheckOutDate()) // Condition 3: Check if the check-in date of the booking request is after the check-in date and before the check-out date of any existing booking || (bookingRequest.getCheckInDate().isAfter(existingBooking.getCheckInDate())&& bookingRequest.getCheckInDate().isBefore(existingBooking.getCheckOutDate())) // Condition 4: Check if the booking request overlaps with any existing booking || (bookingRequest.getCheckInDate().isBefore(existingBooking.getCheckOutDate())&& bookingRequest.getCheckOutDate().isAfter(existingBooking.getCheckInDate())) // Condition 5: Check if the check-in date of the booking request is equal to the check-out date and the check-out date is equal to the check-in date of any existing booking || (bookingRequest.getCheckInDate().equals(existingBooking.getCheckOutDate())&& bookingRequest.getCheckOutDate().equals(existingBooking.getCheckInDate())) // Condition 6: Check if the check-in date of the booking request is equal to the check-out date and the check-out date is equal to the check-in date of the booking request || (bookingRequest.getCheckInDate().equals(existingBooking.getCheckOutDate())&& bookingRequest.getCheckOutDate().equals(bookingRequest.getCheckInDate())) ); }}
BookingDTO.java
package com.Yash.Astoria.dto;import com.Yash.Astoria.entities.Room;import com.Yash.Astoria.entities.User;import com.fasterxml.jackson.annotation.JsonInclude;import jakarta.persistence.FetchType;import jakarta.persistence.JoinColumn;import jakarta.persistence.ManyToOne;import jakarta.validation.constraints.Future;import jakarta.validation.constraints.Min;import jakarta.validation.constraints.NotNull;import lombok.Data;import java.time.LocalDate;@Data@JsonInclude(JsonInclude.Include.NON_NULL)public class BookingDTO { private Long id; private LocalDate checkInDate; private LocalDate checkOutDate; private int numOfAdults; private int numOfChildren; private int totalNumOfGuests; private String bookingConfirmationCode; private UserDTO user; private RoomDTO room;}
StackTrace in Console:
2024-08-19T22:35:34.930+05:30 INFO 19264 --- [Astoria] [nio-8081-exec-5] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 msjakarta.validation.ConstraintViolationException: Validation failed for classes [com.Yash.Astoria.entities.Booking] during persist time for groups [jakarta.validation.groups.Default, ]List of constraint violations:[ ConstraintViolationImpl{interpolatedMessage='check out date must be in the future', propertyPath=checkOutDate, rootBeanClass=class com.Yash.Astoria.entities.Booking, messageTemplate='check out date must be in the future'}] at org.hibernate.boot.beanvalidation.BeanValidationEventListener.validate(BeanValidationEventListener.java:151) at org.hibernate.boot.beanvalidation.BeanValidationEventListener.onPreInsert(BeanValidationEventListener.java:81) at org.hibernate.action.internal.EntityIdentityInsertAction.preInsert(EntityIdentityInsertAction.java:201) at org.hibernate.action.internal.EntityIdentityInsertAction.execute(EntityIdentityInsertAction.java:79) at org.hibernate.engine.spi.ActionQueue.execute(ActionQueue.java:670) at org.hibernate.engine.spi.ActionQueue.addResolvedEntityInsertAction(ActionQueue.java:291) at org.hibernate.engine.spi.ActionQueue.addInsertAction(ActionQueue.java:272) at org.hibernate.engine.spi.ActionQueue.addAction(ActionQueue.java:322) at org.hibernate.event.internal.AbstractSaveEventListener.addInsertAction(AbstractSaveEventListener.java:391) at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:305) at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:224) at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:137) at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:175) at org.hibernate.event.internal.DefaultPersistEventListener.persist(DefaultPersistEventListener.java:93) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:77) at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:54) at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:127) at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:757) at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:741) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:364) at jdk.proxy4/jdk.proxy4.$Proxy132.persist(Unknown Source) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:319) at jdk.proxy4/jdk.proxy4.$Proxy132.persist(Unknown Source) at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:629) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:277) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:170) at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:158) at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:516) at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285) at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:628) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:168) at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:143) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:70) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:379) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:138) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:165) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:223) at jdk.proxy4/jdk.proxy4.$Proxy143.save(Unknown Source) at com.Yash.Astoria.services.impl.BookingService.saveBooking(BookingService.java:60) at com.Yash.Astoria.controllers.BookingController.saveBookings(BookingController.java:23) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:354) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.proceed(AuthorizationManagerBeforeMethodInterceptor.java:269) at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.attemptAuthorization(AuthorizationManagerBeforeMethodInterceptor.java:264) at org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.invoke(AuthorizationManagerBeforeMethodInterceptor.java:197) at org.springframework.security.config.annotation.method.configuration.PrePostMethodSecurityConfiguration$DeferringMethodInterceptor.invoke(PrePostMethodSecurityConfiguration.java:200) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:768) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:720) at com.Yash.Astoria.controllers.BookingController$$SpringCGLIB$$0.saveBookings(<generated>) at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) at java.base/java.lang.reflect.Method.invoke(Method.java:580) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:255) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:188) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:118) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:926) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:831) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1089) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:914) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:590) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:195) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:110) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:164) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:140) at org.springframework.web.filter.CompositeFilter$VirtualFilterChain.doFilter(CompositeFilter.java:108) at org.springframework.security.web.FilterChainProxy.lambda$doFilterInternal$3(FilterChainProxy.java:231) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:365) at org.springframework.security.web.access.intercept.AuthorizationFilter.doFilter(AuthorizationFilter.java:100) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:374) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:126)
[postman screenshot 500 internal server error] (https://i.sstatic.net/8M5SUQsT.png)
Expectation: validating the condition that checkOutDate should be greater than checkInDate
Also, I have tried
@FutureOrPresent
annotation, and it gives the same error