Spring Data Rest and Oracle Between 2 dates query giving nothing - oracle

Spring Data rest is not able to fetch the data in between 2 dates from data base table.
Collection<XXXX> findByCreatedOnBetween(LocalDate fromDate, LocalDate todayDate);
From Bean
private LocalDate createdOn;
Collection<XXXX> findByCreatedOnBetween(LocalDate fromDate, LocalDate todayDate);
private LocalDate createdOn;
I want the data in between 2 dates:
SELECT
*
FROM
testing testing
WHERE
testing.created_on BETWEEN ? AND ? ;

I believe Spring Data Rest accepts only ISO 8601 date format by default (like 2018-10-22).
If you want to accept the date in different format you need to add a converter.
#Configuration
public class RepositoryRestConfig extends RepositoryRestConfigurerAdapter {
#Autowired
CustomDateConverter customDateConverter;
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(customDateConverter);
super.configureConversionService(conversionService);
}
}
#Component
public class CustomDateConverter implements Converter<String, LocalDate > {
#Override
public LocalDate convert(String source) {
return LocalDate.from(DateTimeFormatter.ofPattern("dd-MMM-yy").parse(source));
}
}

Related

Is there a way to configure LocalDate format for serializing and deserializing in the whole spring application?

I have the following problem I hope someone can give me a hand:
Context: 3 Rest endpoints
Create (register)
Find (findKid)
Report (listDashboardInfo)
Requirement: Use the same date format yyyyMMdd for LocalDates in the whole application
Problem: Using #DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN) works for register and listDashboardInfo but not for findKid
These are the relevant parts of the code:
BODY
{
"sailDate": "20191201"
}
#PostMapping(KID_PATH)
#ResponseStatus(HttpStatus.CREATED)
public KidDTO register(#RequestBody #Valid KidDTO kid) {
return kidService.saveKid(kid);
}
GET /kid/0001::20190901
RESPONSE
{
"sailDate": "2019-09-01"
}
#GetMapping(KID_FIND_PATH)
public CompletableFuture<KidDTO> findKid(#PathVariable String id) {
return kidService.findKid(id);
}
GET /kid?shipCode=AL&sailDate=20190901
#GetMapping(KID_LIST_PATH)
public CompletableFuture<Slice<DashboardDTO>> listDashboardInfo(#Valid DashboardFilter filter, Pageable pageable) {
return kidService.listKidsWithStatistics(filter, pageable);
}
#Getter
#Setter
public class DashboardFilter {
#NotNull
#DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
private LocalDate sailDate;
}
#Data
public class KidDTO {
#NotNull
#DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
private LocalDate sailDate;
}
Tests I did:
spring.jackson.date-format in application.properties: From https://blog.codecentric.de/en/2017/08/parsing-of-localdate-query-parameters-in-spring-boot/ this just apply for Date not LocalDate.
Using #JsonFormat(pattern = DateUtils.SHORT_DATE_PATTERN) the listDashboardInfo doesn't recognize the format and generates error
From stackoverflow I also found Spring doesn't use Jackson to deserialize query params so:
- I created a #ControllerAdvice with #InitBinder but the method setAsText is never called:
#ControllerAdvice
public class GlobalDateBinder {
#InitBinder
public void binder(WebDataBinder binder) {
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
#Override
public void setAsText(String text) throws IllegalArgumentException {
LocalDate.parse(text, DateUtils.SHORT_DATE_FORMATTER);
}
});
}
}
Also I tried with a #Bean public Formatter<LocalDate> localDateFormatter() but nothing change:
#Bean
public FormattingConversionService conversionService() {
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService(false);
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateUtils.SHORT_DATE_FORMATTER);
registrar.registerFormatters(conversionService);
return conversionService;
}
#Bean
public Formatter<LocalDate> localDateFormatter() {
return new Formatter<LocalDate>() {
#Override
public LocalDate parse(String text, Locale locale) {
return LocalDate.parse(text, DateUtils.SHORT_DATE_FORMATTER);
}
#Override
public String print(LocalDate object, Locale locale) {
return DateUtils.SHORT_DATE_FORMATTER.format(object);
}
};
}
Any one has an idea of what is happening?
how to make the response of findKid be formatted?
How to configure the whole application with the same date format to works in serialization and parsing/deserializing processes?
UPDATE:
I found here https://stackoverflow.com/questions/30871255/spring-boot-localdate-field-serialization-and-deserialization that I can use #JsonFormat for rest controllers (serialize and deserialize) and #DateTimeFormat for ModelView controllers but using both, at the same time, fixed my error so I don't understand why is that behavior if I only have rest controllers. Looks like in my case #DateTimeFormat deserialize and #JsonFormat serialize, is that the expected behavior? Is there any misconfiguration?
you can add this bean to you configuration:
#Bean
public ObjectMapper objectMapper() {
DateTimeFormatter dateFormatter; // create your date formatter
DateTimeFormatter dateTimeFormatter; // create your date and time formatter
ObjectMapper mapper = new ObjectMapper();
SimpleModule localDateModule = new SimpleModule();
localDateModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(formatter));
localDateModule.addSerializer(LocalDate.class,
new LocalDateSerializer(formatter));
localDateModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(dateTimeFormatter));
localDateModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(dateTimeFormatter));
mapper.registerModules(localDateModule);
return mapper;
}
Just set the property spring.jackson.date-format to any format you want inside you application.properties or application.yml.
Example with application.properties:
spring.jackson.date-format=yyyyMMdd
Example with application.yml:
spring:
jackson:
date-format: yyyyMMdd
Source and other available properties: https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

CodecConfigurationException when saving ZonedDateTime to MongoDB with Spring Boot >= 2.0.1.RELEASE

I was able to reproduce my problem with a minimal modification of the official Spring Boot guide for Accessing Data with MongoDB, see https://github.com/thokrae/spring-data-mongo-zoneddatetime.
After adding a java.time.ZonedDateTime field to the Customer class, running the example code from the guide fails with a CodecConfigurationException:
Customer.java:
public String lastName;
public ZonedDateTime created;
public Customer() {
output:
...
Caused by: org.bson.codecs.configuration.CodecConfigurationException`: Can't find a codec for class java.time.ZonedDateTime.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46) ~[bson-3.6.4.jar:na]
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63) ~[bson-3.6.4.jar:na]
at org.bson.codecs.configuration.ChildCodecRegistry.get(ChildCodecRegistry.java:51) ~[bson-3.6.4.jar:na]
This can be solved by changing the Spring Boot version from 2.0.5.RELEASE to 2.0.1.RELEASE in the pom.xml:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.1.RELEASE</version>
</parent>
Now the exception is gone and the Customer objects including the ZonedDateTime fields are written to MongoDB.
I filed a bug (DATAMONGO-2106) with the spring-data-mongodb project but would understand if changing this behaviour is not wanted nor has a high priority.
What is the best workaround? When duckduckgoing for the exception message I find several approaches like registering a custom codec, a custom converter or using Jackson JSR 310. I would prefer to not add custom code to my project to handle a class from the java.time package.
Persisting date time types with time zones was never supported by Spring Data MongoDB, as stated by Oliver Drotbohm himself in DATAMONGO-2106.
These are the known workarounds:
Use a date time type without a time zone, e.g. java.time.Instant. (It is generally advisable to only use UTC in the backend, but I had to extend an existing code base which was following a different approach.)
Write a custom converter and register it by extending AbstractMongoConfiguration. See the branch converter in my test repository for a running example.
#Component
#WritingConverter
public class ZonedDateTimeToDocumentConverter implements Converter<ZonedDateTime, Document> {
static final String DATE_TIME = "dateTime";
static final String ZONE = "zone";
#Override
public Document convert(#Nullable ZonedDateTime zonedDateTime) {
if (zonedDateTime == null) return null;
Document document = new Document();
document.put(DATE_TIME, Date.from(zonedDateTime.toInstant()));
document.put(ZONE, zonedDateTime.getZone().getId());
document.put("offset", zonedDateTime.getOffset().toString());
return document;
}
}
#Component
#ReadingConverter
public class DocumentToZonedDateTimeConverter implements Converter<Document, ZonedDateTime> {
#Override
public ZonedDateTime convert(#Nullable Document document) {
if (document == null) return null;
Date dateTime = document.getDate(DATE_TIME);
String zoneId = document.getString(ZONE);
ZoneId zone = ZoneId.of(zoneId);
return ZonedDateTime.ofInstant(dateTime.toInstant(), zone);
}
}
#Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.database}")
private String database;
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private int port;
#Override
public MongoClient mongoClient() {
return new MongoClient(host, port);
}
#Override
protected String getDatabaseName() {
return database;
}
#Bean
public CustomConversions customConversions() {
return new MongoCustomConversions(asList(
new ZonedDateTimeToDocumentConverter(),
new DocumentToZonedDateTimeConverter()
));
}
}
Write a custom codec. At least in theory. My codec test branch is unable to unmarshal the data when using Spring Boot 2.0.5 while working fine with Spring Boot 2.0.1.
public class ZonedDateTimeCodec implements Codec<ZonedDateTime> {
public static final String DATE_TIME = "dateTime";
public static final String ZONE = "zone";
#Override
public void encode(final BsonWriter writer, final ZonedDateTime value, final EncoderContext encoderContext) {
writer.writeStartDocument();
writer.writeDateTime(DATE_TIME, value.toInstant().getEpochSecond() * 1_000);
writer.writeString(ZONE, value.getZone().getId());
writer.writeEndDocument();
}
#Override
public ZonedDateTime decode(final BsonReader reader, final DecoderContext decoderContext) {
reader.readStartDocument();
long epochSecond = reader.readDateTime(DATE_TIME);
String zoneId = reader.readString(ZONE);
reader.readEndDocument();
return ZonedDateTime.ofInstant(Instant.ofEpochSecond(epochSecond / 1_000), ZoneId.of(zoneId));
}
#Override
public Class<ZonedDateTime> getEncoderClass() {
return ZonedDateTime.class;
}
}
#Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.database}")
private String database;
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private int port;
#Override
public MongoClient mongoClient() {
return new MongoClient(host + ":" + port, createOptions());
}
private MongoClientOptions createOptions() {
CodecProvider pojoCodecProvider = PojoCodecProvider.builder()
.automatic(true)
.build();
CodecRegistry registry = CodecRegistries.fromRegistries(
createCustomCodecRegistry(),
MongoClient.getDefaultCodecRegistry(),
CodecRegistries.fromProviders(pojoCodecProvider)
);
return MongoClientOptions.builder()
.codecRegistry(registry)
.build();
}
private CodecRegistry createCustomCodecRegistry() {
return CodecRegistries.fromCodecs(
new ZonedDateTimeCodec()
);
}
#Override
protected String getDatabaseName() {
return database;
}
}

Spring boot + DocumentDB custom Query for searching documents

I have Spring Boot + DocumentDB application in which we need to implement a search API, The Search fields are part of nested Json are as below:
{
"id":"asdf123a",
"name":"XYZ",
add{
"city":"ABC"
"pin":123456,
}
}
I need to search with name="XYZ" and city="ABC", I'm trying with below code but somehow not able to retrieve the record.
But I'm able to retrieve with just by Name or ID, but not with name and city or just city which is part of nested JSON.
Employee{
private String id;
private String name;
private Address add
/*Getter and Setters {} */
}
Address{
private String city;
private Long pin;
/*Getter and Setters {} */
}
public class EmployeeController {
EmployeeRepository repository;
#Autowire
EmployeeController (EmployeeRepository repository){
this.repository = repository;
}
#GetMapping(value = "/search", produces = APPLICATION_JSON_VALUE_WITH_UTF8)
public ResponseEntity<?> Search (#RequestParam ("name")String name,
#RequestParam("city") String city){
return new ResponseEntity <> (repository
.findByNameLikeAndAddressCityLike(
name, city),
HttpStatus.OK
);
}
}
#Repository
public interface EmployeeRepository extends DocumentDbRepository<Employee,
String> {
Optional<Employee>findByNameLike(String name); // Perfectly working
Optional<Employee>findByAddressCityLike(String city); // Not working
Optional<Employee>findByNameLikeAndAddressCityLike(String name, String
city); // Not Working
}
Also Just like Spring JPA we use #Query to fire custom/ native query are there any DocumentDB Annotation present if so please guide me with example or Docuemnt. Looking for help

Change datetime string format in request url when using Spring data rest with QuerydslPredicateExecutor

In my domain class I have a field:
public class Reservation {
private LocalDateTime created = LocalDateTime.now();
In my repository I want to find only Reservations with some specific date (time doesn't matter):
public interface ReservationRepository extends Repository<Reservation, Long>, QuerydslPredicateExecutor<Reservation>, QuerydslBinderCustomizer<QReservation> {
bindings.bind(root.created).first((path, value) -> path.between(value.withMinute(0).withHour(0), value.withMinute(0).withHour(0).plusDays(1).minusSeconds(1)));
}
}
Now it works with this url:
/reservations?created=01/20/16 00:00 AM"
But I want to use this datatime format:
2016-01-20T00:00
As I understood the problem that Spring boot uses RepositoryRestMvcConfiguration.class for autoconfiguration. And by default TemporalAccessorParser.class uses some default DateTimeFormatter. And I want to change it to
DateTimeFormatter ISO_LOCAL_DATE_TIME
If only #DateTimeFormat annotation did not help, try to add to the project a custom Converter:
public class CustomStringToLocalDateTime implements Converter<String, LocalDateTime> {
#Override
public LocalDateTime convert(String source) {
return LocalDateTime.parse(source);
}
}
#Configuration
public class RepoRestConfig extends RepositoryRestConfigurerAdapter {
#Override
public void configureConversionService(ConfigurableConversionService conversionService) {
conversionService.addConverter(String.class, LocalDateTime.class, new CustomStringToLocalDateTime());
super.configureConversionService(conversionService);
}
}
Such an approach works in my project (except I had to convert string representation of date ('yyyy-MM-dd') to Instant (yyyy-MM-ddThh:mm:ssZ)).

How to disply timdstamp column with specific format in Spring Data JPA

I am using Spring Data JPA and I have a table like below:
public class Apk {
#Temporal(TemporalType.TIMESTAMP)
#Column
private java.util.Date creationTime;
}
My DBMS is MySQL5.x and the above column is defined datetime type in it. I just call findAl() method in a repository class extends PaginAndSortingRepository.
public interface ApksRepository extends PaginAndSortingRepository<Apk, Long>{
}
public class ApksServiceImpl implements ApksService {
public PagingRes<Apk> findAll(PageInfo pageInfo){
PaginRes<Apk> result = new PagingRes<Apk>();
Page page = apksRepos.findAll(pageInfo.toPageRequest());
result.fromPage(page);
return result;
}
}
public class PageInfo {
private int page;
private int rp;
private String sortname;
private String sortorder;
private String query;
private String qtype;
//getters and setters
public PageRequest toPageRequest() {
Sort.Direction direction = Sort.Direction.ASC;
if (sortorder!=null && "DESC".equals(sortorder))
direction = Sort.Direction.DESC;
return new PageRequest(page-1, rp, direction, sortname);
}
}
public class PagingRes<T> {
private long total;
private int page;
private int rowPerPage;
private List<T> rows;
//getters and setters
public PagingRes<T> fromPage(Page page) {
this.page = page.getNumber();
this.rowPerPage = page.getSize();
this.total = page.getTotalElements();
this.rows = page.getCotent();
return this;
}
}
And I am trying to display data in the table including the column but when I did it, the column is shown as long type. I wan to display the column in the fomat 'dd-MM-yyyy hh:mm:ss'. How can I do this?
Thanks in Advance.
I knew that Jackson mapper for JSON response were giving weird result. So after searching, I used JsonSerializer to fix it like below:
Entity class
#JsonSerialize(using=AuDateSerializer.class)
private java.util.Date eventTime;
Custom Serializer
public class AuDateSerializerextends JsonSerializer<Date> {
#Override
public void serialize(Date value, JsonGenerator gen, SerializerProvider provider) throws IOException, JsonProcessingException {
DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss");
jgen.writeString(formatter.format(value));
}
}
Now it works fine. Thanks.

Resources