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;
}
}
Related
The browser sends the following object to the backend:
Now I would like to store the data in my database. So I have an entity that looks like this:
#Entity
public class NewQuote {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String description;
#ElementCollection(targetClass = Details.class)
private List<Details> details = new ArrayList<>();
public NewQuote(String description, ArrayList<Details> details) {
this.description = description;
this.details = details;
}
#Embeddable
public class Details {
private String description;
private String label;
public Details(String description, String label) {
this.description = description;
this.label = label;
}
public Details() {}
}
Controller
#PostMapping(value = "/save-quote", produces = MediaType.APPLICATION_JSON_VALUE)
public void saveQuote(#RequestBody Map<String, String> data) {
newQuoteService.saveQuote(data);
}
Service
public void saveQuote(Map<String, String> data) {
JSONObject json = new JSONObject(data);
NewQuote newQuote = new NewQuote(
json.getAsString("description"),
json.getAsString("details")
);
newQuoteRepository.save(newQuote);
}
I am getting an error because json.getAsString("details") should not be a string of course. So how can I turn it to ArrayList<Details>?
Add a DTO to manage your json response. You don't need to explicitly use JSONObject because spring already manage the process of mapping under the wood with Jackson.
Also, it is not a good practice to pass your Entities directly into the controller methods.
NewQuoteDto.java
public class NewQuoteDto {
private Long id;
private String description;
private List<Details> details = new ArrayList<>();
public NewQuoteDto() {
}
// getter and setter or you can use lombok
}
DetailDto.java
public class DetailDto {
private String description;
private String label;
public DetailDto() {}
// getter and setter or you can use lombok
}
Controller
#PostMapping(value = "/save-quote", produces = MediaType.APPLICATION_JSON_VALUE)
public void saveQuote(#RequestBody NewQuoteDto dataDto) {
// here you map dataDto to your model before you call saveQuote
NewQuote data = mapper.map(dataDto, NewQuote.class); // if you use ModelMapper library.
newQuoteService.saveQuote(data);
}
For custom mapping take look here.
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;
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
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)".
I have made restful API Using java hibernate jersery Framework.
I have to post data I have done it but I'm missing with one of the column that is MealTypeName.
Here is my DAO Class:
public class MealTypeDAO {
public void addMealType( MealType bean) {
Session session = SessionUtil.getSession();
Transaction tx = session.beginTransaction();
addMealType(session, bean);
tx.commit();
session.close();
}
private void addMealType(Session session, MealType bean){
MealType mealType = new MealType();
mealType.setMealTypename(bean.getMealTypename());
mealType.setModifiedon(bean.getModifiedon());
mealType.setModifiedby(bean.getModifiedby());
session.save(mealType);
}
Here is my resource class:
public class MealTypeResource {
#POST
#Path("/create")
#Consumes("application/json")
public Response addMealType(MealType meal){
meal.setMealTypename(meal.getMealTypename());
meal.setModifiedon(meal.getModifiedon());
meal.setModifiedby(meal.getModifiedby());
MealTypeDAO dao = new MealTypeDAO();
dao.addMealType(meal);
return Response.ok().build();
}
#GET
#Produces("application/json")
public Response getMealType() {
MealTypeDAO dao = new MealTypeDAO();
List mealTypes = dao.getMealType();
String json = new Gson().toJson(mealTypes);
return Response.ok().entity(json.toString()).build();
}
This is my entity class:
public class MealType {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int MealTypeId;
#Column
private String MealTypename;
#Column
private int modifiedby;
#Column
private String modifiedon;
public int getMealTypeId() {
return MealTypeId;
}
public void setMealTypeId(int mealTypeId) {
MealTypeId = mealTypeId;
}
public String getMealTypename() {
return MealTypename;
}
public void setMealTypename(String mealTypename) {
MealTypename = mealTypename;
}
public int getModifiedby() {
return modifiedby;
}
public void setModifiedby(int modifiedby) {
this.modifiedby = modifiedby;
}
public String getModifiedon() {
return modifiedon;
}
public void setModifiedon(String modifiedon) {
this.modifiedon = modifiedon;
}
MySQL DB:
CREATE TABLE `mealtype`(`Mealtypeid` int(11) NOT NULL AUTO_INCREMENT,`MealTypename` varchar(20) DEFAULT NULL,`modifiedby` int(11) NOT NULL,`modifiedon` datetime NOT NULL,PRIMARY KEY (`Mealtypeid`)) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8;
Now M posting these data in JSON FORMAT From POSTMAN:
{"MealTypeId":14,"MealTypename":"adsdf","modifiedby":1,"modifiedon":"2000-01-01 00:00:00"}
And M getting these data:
{"MealTypeId":14,"modifiedby":1,"modifiedon":"2000-01-01 00:00:00"}
MealTypename is missing. How so? Can someone help me out?
You are using names with the first letter in the upper case MealTypename — this is a reason.
The getter with name getMealTypename is used for a JSON property mealTypename (not MealTypename):
public String getMealTypename() {
return MealTypename;
}
You need to specify a JSON property name:
#JsonProperty("MealTypename") — for Jackson
#SerializedName("MealTypename") — for Gson
You need to put this annotation to the field or getter of the class which you mapping to JSON (MealType).
And use the standard Java naming convention.
public class MealType {
#Id
#GeneratedValue(strategy=GenerationType.IDENTITY)
private int mealTypeId;
#Column
private String mealTypename;
}
And this looks really strange:
meal.setMealTypename(meal.getMealTypename());
meal.setModifiedon(meal.getModifiedon());
meal.setModifiedby(meal.getModifiedby());