How to pass local date in path variable in Spring Boot? - java

I'm writing REST service.
I want to get all records by date that I pass in #Path variable.
How Can I do that?
What I tried to do:
Model Class:
#Entity
#Table(name = "test")
public class Test {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate beginDate;
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private LocalDate endDate;
private String activity;
}
Repository:
#Repository
public interface TestRepository extends JpaRepository<Test, Integer> {
List<Test> findAllByName(String name);
List<Test> findAllByBeginDate(LocalDate date);
}
Service:
#Service
public class TestService {
#Autowired
private final TestRepository testRepository;
public TestService(TestRepository testRepository) {
this.testRepository = testRepository;
}
public List<Test> getAllTestsByBeginDate(LocalDate date) {
return testRepository.findAllByBeginDate(date);
}
}
Controller:
#RestController
#RequestMapping("/api/v1/")
public class TestController {
#GetMapping("test/all/{date}")
public List<Test> getAllTestsByBeginDate(#PathVariable ("date") #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
return testService.getAllTestsByBeginDate(date);
}
}
When I pass date like this, I get errors:

This should work
#RestController
#RequestMapping("/api/v1/")
public class TestController {
#GetMapping("test/all/{date}")
public List<Test> getAllTestsByBeginDate(#PathVariable #DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate date) {
return testService.getAllTestsByBeginDate(date);
}
}
or this link will help

You can global configure any datetime format in application properties. Like:
spring.mvc.format.date=yyyy-MM-dd
spring.mvc.format.date-time=yyyy-MM-dd HH:mm:ss
spring.mvc.format.time=HH:mm:ss

Related

LocalDate with JPA does not work correctly

I do request localhost:8080/history/world/2020-02-08
Entity:
public class DailyStatistic {
...
#Column(columnDefinition = "DATE")
private LocalDate date;
...
Controller:
#GetMapping("/world/{date}")
public ResponseEntity<List<DailyStatistic>> getWorldStatByDate(#PathVariable String date) {
List<DailyStatistic> worldStatList = null;
try {
worldStatList = dataProvider.getWorldStatByDate(LocalDate.parse(date));
...
Invoked dataProvider method:
public List<DailyStatistic> getWorldStatByDate(LocalDate date) throws NoDataException {
List<DailyStatistic> dailyStatisticList = repository.findAllByDate(date);
...
Invoked repository method:
#Repository
public interface DailyStatRepository extends JpaRepository<DailyStatistic, Long> {
List<DailyStatistic> findAllByDate(LocalDate date);
Json answer:
{
...
"date": "2020-02-07",
...
}
Remind input: localhost:8080/history/world/2020-02-08
So I get a wrong resultSet. Anybody knows why it happens and how it solve?
Try with this way in your entity
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.time.LocalDateTime;
#JsonSerialize(converter = LocalDateTimeToStringConverter.class)
#JsonDeserialize(converter = StringToLocalDatetimeConverter.class)
private LocalDateTime date;
See this
Or simply this way
#JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate date;

Mapping 2 String fields to OffsetDateTime with MapStruct

I have the following DTO which has 2 fields that must be converted to OffsetDateTime:
#Data
public class AppointmentDTO {
private String id;
#NotNull
private String startTime;
#NotNull
private String endTime;
#NotNull
private String timeZoneStart;
#NotNull
private String timeZoneEnd;
// other fields
}
to
#Data
#Document
#NoArgsConstructor
#AllArgsConstructor
public class Appointment {
#Id
private String id;
private String timeZoneStart;
private String timeZoneEnd;
private OffsetDateTime startTime;
private OffsetDateTime endTime;
private OffsetDateTime createdTime;
// other fields
}
In order to convert, I need the DTO's timeZone fields plus a DateTimeFormatter. So, my attempt is this:
#Component
#Mapper(componentModel = "spring")
public interface IAppointmentMapper {
#Mapping(target = "createdTime", ignore = true)
Appointment convertAppointmentDTOToAppointment(AppointmentDTO dto, #Context OffsetDateTimeMapper offsetDateTimeMapper);
}
public class OffsetDateTimeMapper {
private String startTime;
private String endTime;
private String timeZoneStart;
private String timeZoneEnd;
private final DateTimeFormatter dateTimeFormatter;
public OffsetDateTimeMapper(DateTimeFormatter dateTimeFormatter) {
this.dateTimeFormatter = dateTimeFormatter;
}
#BeforeMapping
public void beforeStartTimeMapping(AppointmentDTO dto) {
this.startTime = dto.getStartTime();
this.timeZoneStart = dto.getTimeZoneStart();
}
#BeforeMapping
public void beforeEndTimeMapping(AppointmentDTO dto) {
this.endTime = dto.getEndTime();
this.timeZoneEnd = dto.getTimeZoneEnd();
}
#AfterMapping
public void startTimeMap(#MappingTarget Appointment appointment) {
LocalDateTime localDateTime = LocalDateTime.parse(startTime, dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(timeZoneStart));
appointment.setStartTime(zonedDateTime.toOffsetDateTime());
}
#AfterMapping
public void endTimeMap(#MappingTarget Appointment appointment) {
LocalDateTime localDateTime = LocalDateTime.parse(endTime, dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(timeZoneEnd));
appointment.setEndTime(zonedDateTime.toOffsetDateTime());
}
}
When I build, however, I get the following error: Error:(22,17) java: Can't map property "java.lang.String startTime" to "java.time.OffsetDateTime startTime". Consider to declare/implement a mapping method: "java.time.OffsetDateTime map(java.lang.String value)". If I put a default map() method on the interface, I don't get the error, but I don't have the proper #Context required.
/*default OffsetDateTime map(String value) {
LocalDateTime localDateTime = LocalDateTime.parse(value);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneOffset.UTC);
return zonedDateTime.toOffsetDateTime();
}*/
Your approach is interesting. In order to work properly you would have to explicitly ignore the properties that you don't want MapStruct to automatically map.
In this case add:
#Mapping(target = "startTime", ignore = true)
#Mapping(target = "endTime", ignore = true)
However, in your case I would try to use Mapping method selection based on qualifiers and use the source parameters as source.
So something like:
#Component
#Mapper(componentModel = "spring")
public interface IAppointmentMapper {
#Mapping(target = "createdTime", ignore = true)
#Mapping(target = "startTime", source = "dto", qualifiedByName = "startTime")
#Mapping(target = "endTime", source = "dto", qualifiedByName = "endTime")
Appointment convertAppointmentDTOToAppointment(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter);
#Named("startTime")
default OffsetDateTime mapStartTime(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter) {
LocalDateTime localDateTime = LocalDateTime.parse(dto.getStartTime(), dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(dto. getTimeZoneStart()));
return zonedDateTime.toOffsetDateTime()
}
#Named("endTime")
default OffsetDateTime mapEndTime(AppointmentDTO dto, #Context DateTimeFormatter dateTimeFormatter) {
LocalDateTime localDateTime = LocalDateTime.parse(dto.getEndTime(), dateTimeFormatter);
ZonedDateTime zonedDateTime = localDateTime.atZone(ZoneId.of(dto. getTimeZoneEnd()));
return zonedDateTime.toOffsetDateTime()
}
}
Note: #Named is org.mapstruct.Named.

How to write JUnit test with hardcoded object possesing LocalDate

When I'm trying to test my Api Controller with hardcoded Object everything is fine unitil I try to add LocalDate parameter to Object.
My Test:
#RunWith(SpringRunner.class)
#WebMvcTest(ApiTransitController.class)
public class ApiTransitControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private TestService testService;
#MockBean
private ReportsService reportsService;
#MockBean
private TransitService transitService;
#Test
public void shouldCreateTransit() throws Exception {
Transit transit = new Transit("London", "Paris", 12L,
LocalDate.of(2018,10,12));
ObjectMapper objectMapper = new ObjectMapper();
String transitJsonString = objectMapper.writeValueAsString(transit);
this.mockMvc.perform(post("/api/transit")
.contentType(MediaType.APPLICATION_JSON)
.content(transitJsonString))
.andExpect(status().isCreated());
verify(transitService).addTransit(eq(new Transit("London", "Paris", 12L,
LocalDate.of(2018,10,12))));
}
}
Model:
#Entity
public class Transit {
#Column(name = "id")
#Id
#GeneratedValue
private Long id;
private String sourceAdress;
private String destinationAdress;
private Long price;
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate date;
#JsonSerialize(using=DistanceSerializer.class)
private Long distance;
public Transit(String sourceAdress, String destinationAdress, Long price, LocalDate date) {
this.sourceAdress = sourceAdress;
this.destinationAdress = destinationAdress;
this.price = price;
this.date = date;
}
//getters and setters, equals and hashCode and toString
Api Controller:
#PostMapping("/api/transit")
#ResponseStatus(HttpStatus.CREATED)
public void createTransit(#RequestBody Transit transit){
LOG.info("Saving transit={}", transit);
transitService.addTransit(transit);
}
I tried adding DateTimeFormmater and few other ways, but still I cant pass the test. Thank you for your time.
Try changing this line
verify(transitService).addTransit(eq(new Transit("London", "Paris", 12L,
LocalDate.of(2018,10,12))));
to this:
verify(transitService).addTransit(eq(transit));
The two objects aren't equal, also you don't need to create a new object you can use already created one.
I added JsonSerializer to date of the Model:
Model:
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
#JsonSerialize(using=DateSerializerNumberTwo.class)
private LocalDate date;
Serializer:
public class DateSerializerNumberTwo extends StdSerializer<LocalDate> {
private static DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd");
public DateSerializerNumberTwo(){
this(null);
}
protected DateSerializerNumberTwo(Class<LocalDate> t){
super(t);
}
#Override
public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(formatter.format(value));
}
}
And the test passes without any changes in the test code. I think it's beacuse the Json default response of date is "yyyy, mm, dd", not like Local date (yyyy-mm-dd)".

Spring Data Redis - Issue while storing Date

I am using Spring Boot + Spring data Redis example to save Date into the Redis Cache. Although I used #DateTimeFormat #JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd"), but still persistance happening is long value. Look like its a millisecond.
Can somebody guide if I need to set extra configurations to persist date like yyyy-MM-dd.
HGETALL users:1
1) "_class"
2) "com.XXX.entity.User"
3) "userId"
4) "1"
5) "name"
6) "John"
7) "createdDate"
8) "1542043247352"
Entity classes:
#Builder
#Data
#AllArgsConstructor
#NoArgsConstructor
#RedisHash("users")
public class User {
#Id
private Long userId;
private String name;
#DateTimeFormat
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date createdDate;
private List<Group> groups;
}
UPDATE-1:: As per suggestion I implemented, but still not working
CustomDateSerializer.java
#Component
public class CustomDateSerializer extends JsonSerializer<Date> {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
#Override
public void serialize(Date date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = dateFormat.format(date);
gen.writeString(formattedDate);
}
}
Custom Interface
#Retention(RetentionPolicy.RUNTIME)
public #interface MyJsonFormat {
String value();
}
Model class
#MyJsonFormat("dd.MM.yyyy")
#JsonSerialize(using = CustomDateSerializer.class)
private Date createdDate;
I'd advise using LocalDateTime (or LocalDate if you prefer) instead. You can then annotate your fields with
#JsonDeserialize(using = LocalDateTimeDeserializer.class)
#JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime createdAt;
using jackson's jsr310 add on:
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
By Using Custom Serializer, this can be solved. Ref #https://kodejava.org/how-to-format-localdate-object-using-jackson/#comment-2027
public class LocalDateSerializer extends StdSerializer<LocalDate> {
private static final long serialVersionUID = 1L;
public LocalDateSerializer() {
super(LocalDate.class);
}
#Override
public void serialize(LocalDate value, JsonGenerator generator, SerializerProvider provider) throws IOException {
generator.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE));
}
}
POJO:
#JsonDeserialize(using = LocalDateDeserializer.class)
#JsonSerialize(using = LocalDateSerializer.class)
private LocalDate createdDate;

Sending date to REST service in JSON

I'm developing a CRUD application with a REST interface in Java-EE.
I have some entities which contains Date fields. When I want to create an instance of such an entity with a POST request with the JSON in the request body, jax-rs (or the underlying deserializer) complains about parsing the date part.
This is the exception I got:
Servlet.service() for servlet service.JAXRSConfiguration threw
exception java.time.format.DateTimeParseException: Text '2011-11-11'
could not be parsed at index 10
I tried to send this in the request:
{
"title": "testTitle",
"description": "testDescription",
"playtime": 50,
"creationDate": "2011-11-11"
}
How should I define the date in the json to get it parsed successfully? Which is the correct format?
Here is the entity class:
#Entity
#NamedQueries({
#NamedQuery(name = "Movie.findAll", query = "SELECT m FROM Movie m")
})
public class Movie extends AbstractDao{
private String title;
private String description;
#ManyToMany(mappedBy = "movies")
private List<Actor> actors;
#ManyToMany(mappedBy = "movies")
private List<Director> directors;
private int playtime;
#OneToOne(cascade = CascadeType.PERSIST)
private Trailer trailer;
#Temporal(value = TemporalType.DATE)
private Date creationDate;
getters,setters, etc
Here is the jax-rs service:
#Path("/movies")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MovieRestService {
#Inject
private MovieService movieService;
#GET
public List<Movie> getMovies(){
return movieService.findAll();
}
#Path("{id}")
#GET
public Movie getMovie(#PathParam("id") long id){
return movieService.findById(id);
}
#POST
public Movie addMovie(Movie movie){
return movieService.create(movie);
}
#Path("{id}")
#PUT
public Movie updateMovie(Movie movie, #PathParam("id") long id){
return movieService.update(movie,id);
}
#Path("{id}")
#DELETE
public Movie deleteMovie(#PathParam("id") long id){
return movieService.delete(id);
}
}
Try to use a DateAdapter (XmlAdapter) like the following one:
And annotate:
#XmlJavaTypeAdapter(DateAdapter.class)
private Date creationDate;
You can also annotate at the getter of creationDate
Class:
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class DateAdapter extends XmlAdapter<String, Date> {
private static final TimeZone UTC = TimeZone.getTimeZone("UTC");
private static final String PATTERN_T_WITH_SEC = "yyyy-MM-dd'T'HH:mm:ss";
private SimpleDateFormat dateFormat = new impleDateFormat(PATTERN_T_WITH_SEC);
#Override
public String marshal(Date v) throws Exception {
dateFormat.setTimeZone(UTC);
String dateF = dateFormat.format(v);
return dateF;
}
#Override
public Date unmarshal(String v) throws Exception {
dateFormat.setTimeZone(UTC);
Date date = dateFormat.parse(v);
return date;
}
}

Categories