How to supply LocalDateTime to a jpa/hibernate query? - postgresql

I'm building a query in my #RepositoryRestResource
where the query looks like this:
#Query("Select DISTINCT comp from InsuranceCompany comp " +
"LEFT JOIN comp.orders ord " +
"wHERE ord.invoiced = false " +
"and (:date is null or :date >= ord.completionTime)"
)
public Page<InsuranceCompany> method(LocalDateTime date, Pageable pageable);
But it throws the following excpetion
Failed to convert from type [java.lang.String] to type [java.time.LocalDateTime] for value '2020-02-14T15:50:24'
when I call the end point with:
GET /method?date=2020-02-14T15:50:24

Mark it with #DateTimeFormat to have Spring converted it correctly:
public Page<InsuranceCompany> method(#DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime date,
Pageable pageable);

Spring by default cannot convert REST parameters to LocalDateTime. You need to provide information on the format of the date, at a parameter level with the #DateTimeFormat annotation, or globally using the DateTimeFormatterRegistrar.
This article explains the two alternatives: https://www.baeldung.com/spring-date-parameters

Option 1: Setting the date/time format globally for all Spring Boot App REST Endpoints
You can configure spring globally to use a certain date / date-time format for your REST endpoints. Suggesting that you use the default Jackson for handling JSON mapping, you can create a configuration class as follows where you set the formats:
#Configuration
public class DateTimeSerializationConfiguration implements Jackson2ObjectMapperBuilderCustomizer {
private static final DateTimeFormatter DATE_FORMATTER = ISO_LOCAL_DATE;
private static final DateTimeFormatter DATE_TIME_FORMATTER = ISO_DATE_TIME;
private static final DateTimeFormatter TIME_FORMATTER = ofPattern("HH:mm");
#Bean
public Formatter<LocalDate> localDateFormatter() {
return new Formatter<LocalDate>() {
#Override
public LocalDate parse(String text, Locale locale) {
return LocalDate.parse(text, DATE_FORMATTER);
}
#Override
public String print(LocalDate object, Locale locale) {
return DATE_FORMATTER.format(object);
}
};
}
#Bean
public Formatter<LocalDateTime> localDateTimeFormatter() {
return new Formatter<LocalDateTime>() {
#Override
public LocalDateTime parse(String text, Locale locale) {
return LocalDateTime.parse(text, DATE_TIME_FORMATTER);
}
#Override
public String print(LocalDateTime object, Locale locale) {
return DATE_TIME_FORMATTER.format(object);
}
};
}
#Bean
public Formatter<LocalTime> localTimeFormatter() {
return new Formatter<LocalTime>() {
#Override
public LocalTime parse(String text, Locale locale) {
return LocalTime.parse(text, TIME_FORMATTER);
}
#Override
public String print(LocalTime object, Locale locale) {
return TIME_FORMATTER.format(object);
}
};
}
#Override
public void customize(Jackson2ObjectMapperBuilder jacksonObjectMapperBuilder) {
jacksonObjectMapperBuilder.serializers(
new LocalDateSerializer(DATE_FORMATTER),
new LocalDateTimeSerializer(DATE_TIME_FORMATTER),
new LocalTimeSerializer(TIME_FORMATTER));
jacksonObjectMapperBuilder.deserializers(
new LocalDateDeserializer(DATE_FORMATTER),
new LocalDateTimeDeserializer(DATE_TIME_FORMATTER),
new LocalTimeDeserializer(TIME_FORMATTER));
}
}
Then, you can create controller methods like this:
#RestController
public class BookingController {
private final YourService yourService;
#Autowired
public BookingController(YourService yourService) {
this.yourService = yourService;
}
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
// Or: with LocalDateTime
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam LocalDateTime dateTime, Pageable pageable) {
return yourService.yourServiceMethod(dateTime, pageable);
}
}
Option 2: Setting the date/time format for each REST Endpoint individually
If you prefer to set the format for each endpoint individually, you have to annotate the request parameter with #DateTimeFormat and specify the expected format. The example below shows different examples on how to accomplish this:
#RestController
public class BookingController {
private final YourService yourService;
#Autowired
public BookingController(YourService yourService) {
this.yourService = yourService;
}
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
// Or: with LocalDateTime
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime dateTime, Pageable pageable) {
return yourService.yourServiceMethod(dateTime, pageable);
}
// Or: with your custom pattern
#GetMapping("/your/api/endpoint")
public YourObject yourControllerMethod(#RequestParam #DateTimeFormat(pattern = "dd.MM.yyyy") LocalDate date, Pageable pageable) {
return yourService.yourServiceMethod(date, pageable);
}
}

Related

Casting an unknown enum value to default enum in spring boot mongo repository actions

I have problem while casting an unknown enum to a default enum in spring boot while using mongo repository.
This is the enum.
public enum EventType implements Serializable
{
WORKDONE("WORKDONE"),
ODRCOM("ODRCOM"),
EXECUTED("EXECUTED"),
REBOOK("REBOOK"),
MANUAL("MANUAL"),
UNKNOWN("UNKNOWN");
private String value;
EventType(final String type) {
this.value = type;
}
#Override
public String toString() {
return value;
}
}
And here is my model class
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Event {
//other properties
#JsonProperty("eventType")
private EventType eventType;
#JsonIgnore
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonProperty("eventType")
public EventType getEventType() {
return eventType;
}
#JsonProperty("eventType")
public void setEventType(String eventType) {
this.eventType = Optional.ofNullable(EventType.valueOf(eventType)).orElse(EventType.UNKNOWN);
}
//other getters and setters
}
Here is the mongo repository
public interface EventRepository extends MongoRepository<Event, String> {
}
The document stored in the db is of the following stucture
{
...
"eventType" : "REBOOK1",
...
}
Please note that the REBOOK1 is not a valid enum. But the setter should be able to able to cast anything else to type UNKNOWN.
However it gives this exception everytime
No enum constant dk.nuuday.ossieventprocessor.app.model.EventType.REBOOK1
I have tried with adding a custom converter as a configuration but no luck
Any help is greatly appreciated

Spring Boot MongoDB findTop5 returns empty array

I am using Spring Boot and MongoDB for a personal project to create a movie and video game database. I have methods that retrieve the latest five entries of each category. When I was testing on a local Tomcat and MongoDB server, the methods work as expected. However, when I deploy to a tomcat server and connect to a MongoDB server, the methods that retrieve the latest five entries always return an empty array. here is the code:
Repository:
public interface MovieRepository extends MongoRepository<Movies, String>{
public List<Movies> findAllByOrderByTitleAsc(Pageable pageable);
#Query("{'title': {$regex: ?0, $options: 'i'}}")
public List<Movies> findByTitle(String title);
public List<Movies> findTop5ByCreatedAtLessThan(String currentDate);
public Movies findMovieById(String id);
public List<Movies> findByPlatformsIn(List<String> platforms);
}
Controller:
#RestController
#RequestMapping("/api")
public class MovieController {
#Autowired
private MovieRepository movieRepository;
#Autowired
private MapValidationErrorService mapValidationErrorService;
#Autowired
private CurrentDateService currentDateService;
#GetMapping("/movies")
public List<Movies> findAllMovies(#RequestParam(value="page", defaultValue="0")int page) {
return movieRepository.findAllByOrderByTitleAsc(PageRequest.of(page, 20));
}
#GetMapping("/movies/{movieId}")
public Movies findMovieById(#PathVariable("movieId") String movieId) {
return movieRepository.findMovieById(movieId);
}
#PostMapping("/movies")
public ResponseEntity<?> saveMovie(#Valid #RequestBody Movies movie, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.mapvalidationService(result);
if (errorMap != null) return errorMap;
movie.setId(null);
Movies newMovie = movieRepository.save(movie);
return new ResponseEntity<Movies>(newMovie, HttpStatus.OK);
}
#PutMapping("/movies")
public ResponseEntity<?> updateMovie(#Valid #RequestBody Movies movie, BindingResult result) {
ResponseEntity<?> errorMap = mapValidationErrorService.mapvalidationService(result);
if (errorMap != null) return errorMap;
Movies updatedMovie = movieRepository.save(movie);
return new ResponseEntity<Movies>(updatedMovie, HttpStatus.OK);
}
#DeleteMapping("/movies/{movieId}")
public void deleteMovieById(#PathVariable("movieId") String movieId) {
movieRepository.deleteById(movieId);
}
#GetMapping("/moviePlatforms")
public List<Movies> findMoviesByPlatforms(#RequestParam(value="platform") List<String>platforms) {
return movieRepository.findByPlatformsIn(platforms);
}
#GetMapping("/newFiveMovies")
public List<Movies> findTop5ByCreatedAt() {
String currentDate = currentDateService.getCurrentDate();
return movieRepository.findByCreatedAt(currentDate);
}
}
I have changed the find top 5 methods to use the #Query annotation, but it yields the same results. All the other methods in the controller and repository work as expected. I have tried using MongoDB on a Mac, and also on MongoDB Atlas. Any help is greatly appreciated.
I figured out that the date I was sending as a parameter was formatted wrong. The date was being stored as "M/d/yy h:mm a", but I was sending "M/dd/yyyy hh:mm a".

CodecConfigurationException Can't find a codec for DateTime In MongoDB

I'm using Mongodb with Spring Boot (1.4.4.RELEASE), I store an object in a MongoDB database and am getting a CodecConfigurationException When i try to get Object with AggregationOperation.
My MongoDbConfig is
#Configuration
public class MongoDbConfig {
#Value("${mongodb_host}")
private String host;
#Value("${mongodb_port}")
private String port;
#Value("${mongodb_name}")
private String dataBase;
#Value("${mongodb_username}")
private String userName;
#Value("${mongodb_password}")
private String password;
public #Bean MongoClient mongoClient() {
return new MongoClient(new ServerAddress("127.0.0.1", 27017), new ArrayList<MongoCredential>() {
{
add(MongoCredential.createCredential(userName, dataBase,password.toCharArray()));
}
});
}
public #Bean MongoDbFactory mongoDbFactory() throws Exception {
return new SimpleMongoDbFactory(mongoClient(), dataBase);
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory());
return mongoTemplate;
}
}
My Document is
#Document(collection = "FuelBook")
public class FuelBook implements Serializable {
private String orgId;
private String branchId;
private String vehicleId;
private String driverName;
private DateTime date;
private boolean isActive = true;
private Double kmsReading;
private float costOfFuel;
private String typeOfFuel;
//getter and setter
}
I am creating Document using below code
public String addFuelBookDetails(AddFuelBookDetailsDTO request,
String orgId, String branchId, String vehicleId) {
try{
if(!mongoTemplate.collectionExists(FuelBook.class)){
mongoTemplate.createCollection(FuelBook.class);
}
FuelBook fuelBook = new FuelBook();
fuelBook.setOrgId(orgId);
fuelBook.setBranchId(branchId);
fuelBook.setVehicleId(vehicleId);
fuelBook.setDriverName(request.getDriverName());
fuelBook.setDate(TimeConversionHelper.getJodaDate(request.getDate(),
"yyyy-MM-dd"));
fuelBook.setCostOfFuel(Float.parseFloat((request.getCostOfFuel())));
fuelBook.setKmsReading(Double.parseDouble(request.getKmsReading()));
fuelBook.setTypeOfFuel(request.getTypeOfFuel());
mongoTemplate.insert(fuelBook, "FuelBook");
return StringConstants.SUCCESS;
} catch(Exception e) {
return null;
}
}
And this code to create String To DateTime
public static DateTime getJodaDate(String dateString, String format) {
DateTime dateTime = null;
try {
SimpleDateFormat sdf = new SimpleDateFormat(format);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = (Date) sdf.parse(dateString);
long timeInMillis = date.getTime();
dateTime = new DateTime(timeInMillis);
} catch (ParseException e1) {
e1.printStackTrace();
}
return dateTime;
}
Using below code try to get All FuelBooks
AggregationOperation org = Aggregation.match(Criteria.where("orgId").is(orgId));
AggregationOperation branch = Aggregation.match(Criteria.where("branchId").is(branchId));
AggregationOperation vehicle = Aggregation.match(Criteria.where("vehicleId").is(vehicleId));
DateTime jodaStartTime = TimeConversionHelper.getJodaDate(request.getStartTime(), "yyyy-MM-dd");
DateTime jodaEndTime = TimeConversionHelper.getJodaDate(request.getEndTime(), "yyyy-MM-dd");
AggregationOperation startTime = Aggregation.match(Criteria.where("date").gte(jodaStartTime));
AggregationOperation endTime = Aggregation.match(Criteria.where("date").lte(jodaEndTime));
AggregationOperation offsetAggregation=Aggregation.skip(offset);
AggregationOperation limitAggregation=Aggregation.limit(limit);
Aggregation aggregation = Aggregation.newAggregation(org, branch, vehicle, startTime, endTime,offsetAggregation,limitAggregation);
AggregationResults<FuelBook> result = mongoTemplate.aggregate(aggregation, "FuelBook" ,FuelBook.class);
if(result!=null && result.getMappedResults()!=null && !result.getMappedResults().isEmpty()) {
List<FuelBook> fuelLists = result.getMappedResults();
However, I'm running into this error message:
org.bson.codecs.configuration.CodecConfigurationException: Can't find a codec for class org.joda.time.DateTime.
at org.bson.codecs.configuration.CodecCache.getOrThrow(CodecCache.java:46)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:63)
at org.bson.codecs.configuration.ProvidersCodecRegistry.get(ProvidersCodecRegistry.java:37)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:210)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeMap(DBObjectCodec.java:220)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:196)
at com.mongodb.DBObjectCodec.encodeIterable(DBObjectCodec.java:269)
at com.mongodb.DBObjectCodec.writeValue(DBObjectCodec.java:198)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:128)
at com.mongodb.DBObjectCodec.encode(DBObjectCodec.java:61)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:63)
at org.bson.codecs.BsonDocumentWrapperCodec.encode(BsonDocumentWrapperCodec.java:29)
at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:253)
at com.mongodb.connection.RequestMessage.addDocument(RequestMessage.java:205)
at com.mongodb.connection.CommandMessage.encodeMessageBodyWithMetadata(CommandMessage.java:75)
at com.mongodb.connection.RequestMessage.encodeWithMetadata(RequestMessage.java:160)
at com.mongodb.connection.CommandProtocol.sendMessage(CommandProtocol.java:184)
at com.mongodb.connection.CommandProtocol.execute(CommandProtocol.java:108)
at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:159)
at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:286)
at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:173)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:215)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:186)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:178)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:91)
at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:84)
at com.mongodb.operation.CommandReadOperation.execute(CommandReadOperation.java:55)
at com.mongodb.Mongo.execute(Mongo.java:773)
at com.mongodb.Mongo$2.execute(Mongo.java:760)
at com.mongodb.DB.executeCommand(DB.java:653)
at com.mongodb.DB.command(DB.java:423)
at com.mongodb.DB.command(DB.java:439)
at com.mongodb.DB.command(DB.java:394)
at org.springframework.data.mongodb.core.MongoTemplate$3.doInDB(MongoTemplate.java:390)
at org.springframework.data.mongodb.core.MongoTemplate$3.doInDB(MongoTemplate.java:388)
at org.springframework.data.mongodb.core.MongoTemplate.execute(MongoTemplate.java:446)
at org.springframework.data.mongodb.core.MongoTemplate.executeCommand(MongoTemplate.java:388)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1556)
at org.springframework.data.mongodb.core.MongoTemplate.aggregate(MongoTemplate.java:1491)
at com.icarat.eshiksha.mongodb.dao.impl.FuelBookDAOImpl.getFuelBookDetails(FuelBookDAOImpl.java:153)
at com.icarat.eshiksha.rest.controller.FuelBookController.getFuelBookDetails(FuelBookController.java:132)
Can anyone can provide some insight on why this is happening? Thank you
I found solution from this link
I change my mongoClient in MongoDbConfig like
public #Bean MongoClient mongoClient() {
BSON.addEncodingHook(DateTime.class, new JodaTimeTransformer());
BSON.addDecodingHook(Date.class, new JodaTimeTransformer());
return new MongoClient(new ServerAddress("127.0.0.1", 27017), new ArrayList<MongoCredential>() {
{
add(MongoCredential.createCredential(userName, dataBase,password.toCharArray()));
}
});
}
And i just add JodaTimeTransformer class into my project classPath

play2 java form binding - how to set field name to map to object?

Say I have the below test case
I want to be able to bind camel case parameters:
anyData.put("my_id", "bob#gmail.com");
How can I get this test to pass??
public class FormBindingExampleTest {
public static class FormBindingExampleModel {
public String myid;
public String email;
public String getMyid() {
return myid;
}
public void setMyid(String myid) {
this.myid = myid;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
Map<String,String> anyData = new HashMap();
anyData.put("my_id", "bob#gmail.com");
anyData.put("email", "secret");
FormBindingExampleModel user = userForm.bind(anyData).get();
System.out.println(user.myid);
assert(user.myid.equals("bob#gmail.com"));
}
}
Use form's fill() method inorder to populate the form with existing value.
#Test
public void itShouldBindForm(){
Form<FormBindingExampleModel> userForm = form(FormBindingExampleModel.class);
FormBindingExampleModel formModel = new FormBindingExampleModel();
formModel.setMyid("bob#gmail.com");
formModel.setEmail("secret");
userForm.fill(formModel);
FormBindingExampleModel user = userForm.get();
System.out.println(user.getMyid);
assert(user.getMyid.equals("bob#gmail.com"));
}
Documentation available here.

JPA, How to find an object that has composite id?

Based on second approach answered here I designed my JPA class.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
SearchKeywordJPA that = (SearchKeywordJPA) o;
if (date != that.date) return false;
if (!keyword.equals(that.keyword)) return false;
if (!userUUID.equals(that.userUUID)) return false;
return true;
}
#Override
public int hashCode() {
int result = keyword.hashCode();
result = 31 * result + (int) (date ^ (date >>> 32));
result = 31 * result + userUUID.hashCode();
return result;
}
#Override
public String toString() {
return "SearchKeywordJPA{" +
"keyword='" + keyword + '\'' +
", date=" + date +
", userUUID='" + userUUID + '\'' +
'}';
}
public String getKeyword() {
return keyword;
}
public void setKeyword(String keyword) {
this.keyword = keyword;
}
public long getDate() {
return date;
}
public void setDate(long date) {
this.date = date;
}
public String getUserUUID() {
return userUUID;
}
public void setUserUUID(String userUUID) {
this.userUUID = userUUID;
}
#Override
public int compareTo(SearchKeywordJPA searchRecord) {
long comparedDate = searchRecord.date;
if (this.date > comparedDate) {
return 1;
} else if (this.date == comparedDate) {
return 0;
} else {
return -1;
}
}
/**********************
* Key class
**********************/
public class SearchKeyId {
private int id;
private int version;
}
}
In my servlet I want to check datastore and store my object if is not exist.
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
...
for(SearchKeywordJPA item: applicationList) {
if(!isRecorded(item))
storeRecord(item);
}
}
private boolean isRecorded(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
SearchKeywordJPA item = em.find(SearchKeywordJPA.class, record);
return item != null;
}
private void storeRecord(SearchKeywordJPA record) {
EntityManager em = EMF.get().createEntityManager();
em.persist(record);
}
However when I run, application crashes and log says
javax.persistence.PersistenceException: org.datanucleus.store.appengine.FatalNucleusUserException: Received a request to find an object of type com.twitterjaya.model.SearchKeywordJPA identified by SearchKeywordJPA{keyword='airasia', date=1335680686149, userUUID='FFFF0000'}. This is not a valid representation of a primary key for an instance of com.twitterjaya.model.SearchKeywordJPA.
What is the reason? any suggestion would be appreciated. Thanks
You pass an instance of the IdClass into em.find ... i.e SearchKeyId. Obviously if you really have an IdClass that has no equals/hashCode/toString/constructor then you will likely get many problems. Those problems will only be increased by using an ancient plugin for GAE/Datastore.
If your Key is
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeywordJPA.SearchKeyId.class)
public class SearchKeywordJPA implements Comparable<SearchKeywordJPA> {
you are doing it wrong.
IdClass does not need any annotation of #IdClass just the #Id
annotation.
Key can not be an entity.
Need to implements Serializable , comparable is not needed
Need to override equals and hascode and have no arg constructor
Class key need to be as follows.
public class SearchKeyId implements Serializable {
private String keyword;
private long date;
And your entity I assume something like this.
#Entity(name = "SearchKeywordJPA")
#IdClass(SearchKeyId.class)
public class SearchKeywordJPA {
#Id
private String keyword;
#Id
private long date;
private String userUUID;
Just consider that find method will use the SearchKey.class to find
the entities.
Fields that are in the IdClass need to have #Id annotation in the entity.
Key can not be an entity on its own.
Comparable is not really needed as all the comparison are placed in the IdClass