I'm trying to send a POST request to an Spring RestController with a request body. In the object there is a Long value but it is not arriving to the endpoint with the other parameters.
The class used as #RequestBody is this one:
#Entity
public class Curso {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name = "id")
private Long id;
#Column(name = "titulo")
private String titulo;
#Column(name = "nivel")
private String nivel;
#Column(name = "nhoras")
private String nhoras;
#Column(name = "profesorid")
private Long profesorid;
[...]
public Long getProfesorid() {
return profesorid;
}
public void setProfesorid(Long profesor) {
this.profesorid = profesorid;
}
}
The endpoint in the class annotated with #RestController, is this:
#RequestMapping(value = "/crear-curso", method = RequestMethod.POST)
public void addCurso(#RequestBody Curso curso) {
cursoService.addCurso(curso);
}
And this is the JSON I'm using in the body of the POST request:
{
"titulo": "Git",
"nivel": "Intermedio",
"nhoras": "12",
"profesorid": 1581068174
}
All the other parameters are arriving correctly and the object arrives to the database, but with the profesorid with null value. I stopped the execution y this addCurso method and the value of profesorid is null. The id value is not being sent in the request because it is setted before saving in the database.
Please, anyone can help me and say what is failing here? Many thanks in advance.
public void setProfesorid(Long profesor) {
this.profesorid = profesorid;
}
Look at this setter. You've made a mistake here.
It should be
public void setProfesorid(Long profesor) {
this.profesorid = profesor;
}
Related
i have this entity class to store movie details.
#Table(name = "movie")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String category;
private double rating;
}
Now i also have a rest api to get by movie id
How do i modify my rest api to return http error code 204, to show no content has been found.
#GetMapping("/movie/{id}")
public Movie getMovieById(#PathVariable(value = "id") Integer id) {
if (service.get(id)!=null) {
return service.get(id);
} else {
return HttpStatus.NO_CONTENT;
}
}
204 No Content is not the appropriate status code here; that means "the resource exists/existed but I am not sending contents". This is specifically the case for 404 Not Found. You can either throw an exception (such as ResponseStatusException) or change your controller to return ResponseEntity<Movie>.
In case asked for content is not available you could stick to 404 Resource Not found. Using some other obscure (not 204) HTTP status code is not a good idea as it can cause browser to react differently.
In your case, returning any empty response body, is also fine.
you can do something like below,
#Table(name = "movie")
public class Movie {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String title;
private String category;
private double rating;
public String status;
}
#GetMapping("/movie/{id}")
public ResponseEntity<Movie> getMovieById(#PathVariable(value = "id") Integer id) {
ResponseEntity<Movie> response = null;
if (service.get(id)!=null) {
Movie movie = service.get(id);
ResponseEntity.ok(movie)
} else {
Movie movie = new Movie()
movie.setStatus("FAILURE");
ResponseEntity response = new ResponseEntity<>(movie, HttpStatus.NOT_FOUND)
}
return response
}
Have the same DTO object for POST and PUT methods:
class AcmeRequest {
private String id;
#NotEmpty
private String productCode;
private String description;
}
For POST request I always expect to see productCode field, that's why I specified #NotEmpty annotation but when PUT request received productCode should be optional.
Is it possible some how just to skip #NotEmpty when request is PUT?
Every Hibernate Validator annotation has a groups parameter. Through interfaces, you can control which validations are activated. See more at docs.
In controller level, specify which groups must be activated with the #Validated annotation.
Below, there is a small example from one of my demo projects. I once had the same question as you.
Entity:
#Entity
#Table(name = "tasks")
#Getter #Setter
public class Task
{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Null(message = "You can't provide a task ID manually. ID's are automatically assigned by our internal systems.", groups = {TaskInsertValidatorGroup.class})
#NotNull(message = "You must provide an id" , groups = TaskUpdateValidatorGroup.class)
private Integer id;
#NotBlank(message = "Task description cannot be empty")
#Length(max = 255 , message = "Task description length must not exceed 255 characters")
private String description;
#JsonProperty("is_completed")
#Column(name = "is_completed")
private Boolean isCompleted = false;
#CreationTimestamp
#JsonProperty("created_on")
#JsonFormat(pattern="dd-MM-yyyy HH:mm:ss")
#Column(name = "created_on", updatable = false)
private Timestamp creationDate;
#UpdateTimestamp
#JsonProperty("last_modified")
#JsonFormat(pattern="dd-MM-yyyy HH:mm:ss")
#Column(name = "last_modidied")
private Timestamp lastModificationDate;
#Override
public boolean equals(Object o)
{
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Task task = (Task) o;
return id.equals(task.id);
}
#Override
public int hashCode()
{
return Objects.hash(id);
}
}
Interfaces:
public interface TaskInsertValidatorGroup {}
public interface TaskUpdateValidatorGroup{}
Controller:
RestController
#RequestMapping("/api")
public class TaskRestController
{
#Autowired
private TaskService taskService;
#GetMapping("/tasks/{id}")
public ResponseEntity<?> getTask(#PathVariable Integer id)
{
return new ResponseEntity<>(taskService.findTask(id),HttpStatus.OK);
}
#GetMapping("/tasks")
public ResponseEntity<?> getTasks()
{
return new ResponseEntity<>(taskService.findAllTasks(),HttpStatus.OK);
}
#PostMapping("/tasks")
public ResponseEntity<?> addTask(#Validated(TaskInsertValidatorGroup.class) #RequestBody Task task)
{
taskService.saveTask(task);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task added")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
#RequestMapping(value = "/tasks" , method = RequestMethod.PATCH)
public ResponseEntity<?> updateTask(#Validated(TaskUpdateValidatorGroup.class) #RequestBody Task task)
{
taskService.updateTask(task);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task Updated")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
#RequestMapping(value = "/tasks/{id}", method = RequestMethod.DELETE)
public ResponseEntity<?> removeTask(#PathVariable Integer id)
{
taskService.removeTask(id);
APISuccessResponse response = APISuccessResponse.builder()
.info("Task Deleted")
.build();
return new ResponseEntity<>(response,HttpStatus.OK);
}
}
I'm working on a Spring Boot + Maven + Restful + Hibernate project! After creating the RestController for adding new Devices in database i'm getting this error:
2018-03-28 10:15:18.786 WARN 9286 --- [nio-9090-exec-9] .w.s.m.s.DefaultHandlerExceptionResolver : Failed to read HTTP message: org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `com.hhm.hsy.hibernate.models.Protocol` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('{"id":5,"protocolName":"ProtocolForTesting","port":5202}')
at [Source: (PushbackInputStream); line: 1, column: 52] (through reference chain: com.hhm.hsy.hibernate.models.Device["protocol"])
Here is my first entity:
#Entity
#Table(name = "devices", catalog = "hqm")
public class Device implements Serializable {
private static final long serialVersionUID = -8311225474375837513L;
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "device_id", unique = true, nullable = false)
private Integer id;
#Column(name = "device_name", unique = true, nullable = false)
private String deviceName;
#ManyToOne
#JoinColumn(name = "protocol_id")
private Protocol protocol;
public Device() {
}
public Device(Integer id, String deviceName, Protocol protocol) {
this.id = id;
this.deviceName = deviceName;
this.protocol = protocol;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getDeviceName() {
return deviceName;
}
public void setDeviceName(String deviceName) {
this.deviceName = deviceName;
}
public Protocol getProtocol() {
return protocol;
}
public void setProtocol(Protocol protocol) {
this.protocol = protocol;
}
And the second entity:
#Entity
#Table(name = "protocols", catalog = "hqm")
public class Protocol implements Serializable {
#Id
#GeneratedValue(strategy = IDENTITY)
#Column(name = "protocol_id", unique = true, nullable = false)
private Integer id;
#Column(name = "protocol_name", unique = true, nullable = false, length = 45)
private String protocolName;
#Column(name = "port", nullable = false)
private Integer port;
#OneToMany(mappedBy = "protocol", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<Device> devices = new HashSet<>();
public Protocol() {
}
public Protocol(Integer id, String protocolName, Integer port) {
this.id = id;
this.protocolName = protocolName;
this.port = port;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProtocolName() {
return protocolName;
}
public void setProtocolName(String protocolName) {
this.protocolName = protocolName;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
#JsonIgnore
public Set<Device> getDevices() {
return devices;
}
#JsonIgnore
public void setDevices(Set<Device> devices) {
this.devices = devices;
}
}
Controller:
#RestController
#RequestMapping(value = "/api/devices")
#ComponentScan({"com.hhm.hsy.pmcs.*"})
public class DevicesController {
#Autowired
#Qualifier(value = "deviceService")
GenericServiceIntf deviceService;
// get ALL DEVICE
#RequestMapping(value = "", method = RequestMethod.GET)
public Map<String, Object> getDevices() {
Map<String, Object> devicesMap = new HashMap<>();
devicesMap.put("devices", deviceService.getAll());
return devicesMap;
}
//save a new DEVICE
#RequestMapping(value = "", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Device> addDevice(#RequestBody Device device) {
deviceService.save(device);
return ResponseEntity.accepted().body(device);
}
}
Service:
#Service("deviceService")
public class DeviceServiceImpl extends GenericServiceAbstractImpl<Device, Integer> implements Serializable{
private static final long serialVersionUID = 697655212967127150L;
#Autowired
public DeviceServiceImpl(#Qualifier("deviceDao") GenericDaoIntf genericDao) {
super(genericDao);
}
}
So when i'm trying to add a new device, i get the error i mentioned upper.I don't know what is causing this exception. When I try to add with post a new Protocol it's working, table is being created in the database correctly and I am getting the data correctly in GET request as well..Please help me, I'm new to springboot and restful... if some more information is required, please just inform me and i will post it! Thank you!
I tried to reproduce your problem: here, but everything works as expected.
I think it can be related with this bug.
You should try to reproduce bug with different jackson version.
EDIT:
One more thing: It looks like you try to construct Protocol instead of Device. Show us your deviceService, if you can.
Failed to read HTTP message:
org.springframework.http.converter.HttpMessageNotReadableException:
JSON parse error: Cannot construct instance of
`com.hhm.hsy.hibernate.models.Protocol
I'm a newbie coder having just finished a 6 month coding crash-course. I'm working on a java webapp to demonstrate my skills, and the project idea I had involves retrieving JSON data from an API, something we didn't learn about in class. I made POJOs to match the JSON, and I'm trying to parse the JSON into java objects to store in a database, however my database tables are never filled with data when I run through the app. I suspect the problem is somewhere with my method to convert the JSON but any feedback is greatly appreciated. Here's all my code I think is relevant, sorry if its TMI. I also apologize if my code is ugly, I'm a beginner... Thanks!
API returns JSON like this:
{
"result":{
"status":1,
"num_results":1,
"total_results":500,
"results_remaining":499,
"matches":[{
"match_id":3188095188,
"match_seq_num":2784956606,
"start_time":1495079320,
"lobby_type":7,
"radiant_team_id":0,
"dire_team_id":0,
"players":[{
"account_id":86920222,
"player_slot":0,
"hero_id":18
},{
"account_id":61122568,
"player_slot":1,
"hero_id":85
},{
"account_id":10208661,
"player_slot":2,
"hero_id":13
},{
"account_id":106083675,
"player_slot":132,
"hero_id":50
}]
}]
}
}
My POJOs:
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Result {
#JsonIgnore
#Id
#GeneratedValue
private int id;
#JsonProperty("status")
private int status;
#JsonProperty("num_results")
private int num_results;
#JsonProperty("total_results")
private int total_results;
#JsonProperty("results_remaining")
private int results_remaining;
#OneToMany
#JoinColumn(name = "result_id")
#ElementCollection(targetClass=Matches.class)
#JsonProperty("matches")
private List<Matches> matches;
// getters and setters
}
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Matches {
#Id
#JsonProperty("match_id")
private int match_id;
#JsonIgnore
#ManyToOne
private Result result;
#JsonProperty("match_seq_num")
private int match_seq_num;
#JsonProperty("start_time")
private int start_time;
#JsonProperty("lobby_type")
private int lobby_type;
#JsonProperty("radiant_team_id")
private int radiant_team_id;
#JsonProperty("dire_team_id")
private int dire_team_id;
#OneToMany
#JoinColumn(name = "Matches_id")
#ElementCollection(targetClass=Players.class)
#JsonProperty("players")
private List<Players> players;
// getters and setters
}
#Entity
#JsonIgnoreProperties(ignoreUnknown = true)
public class Players {
#JsonIgnore
#Id
#GeneratedValue
private int id;
#JsonIgnore
#ManyToOne
private Matches matches;
#JsonProperty("account_id")
private int account_id;
#JsonProperty("player_slot")
private int player_slot;
#JsonProperty("hero_id")
private int hero_id;
// getters and setters
}
Services method to read and convert the JSON to objects (url is censored, don't want my API key to be public)
public class SteamService {
public static Result getMatchHistory(String steamid){
Result result = new Result();
String MatchHistoryUrl = "https:**URL**="+steamid;
RestTemplate restTemplate = new RestTemplate();
Result jsonresult = restTemplate.getForObject(MatchHistoryUrl, Result.class);
return jsonresult;
}
}
Controller
#Controller
#RequestMapping("")
public class HomeController {
#Autowired
private ResultsDao resultsDao;
#RequestMapping(value = "", method = RequestMethod.GET)
public String index(Model model){
model.addAttribute("title", "Welcome");
return "home/home";
}
#RequestMapping(value = "", method = RequestMethod.POST)
public String processSteamIdField(#RequestParam("steamid")String steamid, Model model) {
Result newresult = getMatchHistory(steamid);
resultsDao.save(newresult);
return "redirect:results";
}
}
DAO
#Repository
#Transactional
public interface ResultsDao extends CrudRepository<Result, Integer>{
}
Maybe my approach is a bit naive, but... If you want to store the JSON as string in the database, then I would use an object mapper for this:
new ObjectMapper().writeValueAsString(myObject);
and for reading a JSON and parsing it to a class I would do:
new ObjectMapper().readValue(JSON_STRING_HERE, "utf-8"), MyPOJO.class);
Also, if you already are using Spring, then your controller may look like this (for a POST, for example)
#RequestMapping(value = "/", method = RequestMethod.POST)
public MyPojo myController(#RequestBody MyPojo myBody) {
myRepository.save(myBody);
}
So, the parsing of the JSON that the client is sending to your app and your controller is already handled by Spring
I have two domain models mapped using Hibernate #OneToMany. I am trying to create a JSON object in the frontend and send it to the spring mvc controller to set the model data on its own.
Following are my model classes:
ConceptModelDetails.java
#Entity
#Table(name="conceptModelDetails")
#SequenceGenerator(name="CONCEPT_SEQ",sequenceName="concept_sequence", initialValue=1, allocationSize=1)
public class ConceptModelDetails implements java.io.Serializable{
private static final long serialVersionUID = 1L;
#Id #GeneratedValue(strategy = GenerationType.SEQUENCE, generator="CONCEPT_SEQ")
private int instructionsId;
private String operationType;
private String conceptModelID;
private String requestor;
private String status;
private Timestamp requestDateTime;
private Timestamp lastExecutedDateTime;
private Timestamp completedDateTime;
#OneToMany(fetch = FetchType.EAGER, cascade=CascadeType.ALL, mappedBy="conceptModelDetails")
#JsonManagedReference // nested exception is org.springframework.http.converter.HttpMessageNotWritableException:
//Could not write JSON: Infinite recursion
//The fix is to get Jackson to be able to handle bi-directional references
private List<Instructions> instructions = new ArrayList<Instructions>();
public ConceptModelDetails() {
// TODO Auto-generated constructor stub
}
//setter & getter methods
}
and Instructions.java:
#Entity
#Table(name="instructions")
#SequenceGenerator(name="INSTRUCTIONS_SEQ", sequenceName="instructions_sequence",initialValue=1, allocationSize=1)
public class Instructions implements java.io.Serializable {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator="INSTRUCTIONS_SEQ")
private int Sno;
private String instruction;
#ManyToOne
#JoinColumn(name="instructionsId")
#JsonBackReference
private ConceptModelDetails conceptModelDetails;
//setter & getter methods
}
This is my send method at frontend to create and send the JSON object:
$scope.send = function() {
console.log("test");
var dataObj = {
"operationType" : $scope.operationType,
"conceptModelID" : $scope.conceptID,
"requestor" : $scope.requestor,
"status" : "new",
"requestDateTime" : null,
"lastExecutedDateTime" : null,
"completedDateTime" : null,
"instructions" : null
};
console.log(dataObj);
dataObj.instructions = [];
console.log($scope.operations_publish);
var ins = getSelected();
for ( var i in ins) {
var temp = {
instruction : null,
conceptModelDetails : null
}
temp.instruction = ins[i];
dataObj.instructions.push(temp);
}
var response = $http.post(
'PostService', dataObj);
response.success(function(data, status, headers, config) {
$scope.responseData = data;
});
response.error(function(data, status, headers, config) {
alert("Exception details: " + JSON.stringify({
data : data
}));
});
}
Following is my controller:
#RequestMapping(value = "/PostService", method = RequestMethod.POST)
public #ResponseBody String Test(#RequestBody ConceptModelDetails conceptModelDetails){
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
ConceptModelDAO obj = (ConceptModelDAO) context.getBean("objDAO");
System.out.println("concept id: "+conceptModelDetails.getConceptModelID()+" "+ conceptModelDetails.getInstructionsId());
System.out.println("instructions id: "+conceptModelDetails.getInstructions());
// ConceptModelDAOImpl objDAO = new ConceptModelDAOImpl();
obj.add(conceptModelDetails);
Instructions instructions = new Instructions();
System.out.println("dimba: " + instructions.getInstruction());
ArrayList<Instructions> operations = (ArrayList<Instructions>) conceptModelDetails.getInstructions();
for (int i = 0; i< operations.size(); i++ ) {
instructions.setInstruction(operations.get(i).getInstruction());
instructions.setConceptModelDetails(conceptModelDetails);
obj.addInstructions(instructions);
}
return null;
}
I am getting the eror: 400 (Bad Request) because of List<Instructions> instructions. Please suggest how do I deal with this.
I have found the problem in this code. As explained by Bozho here,
ArrayList<Instructions> operations = (ArrayList<Instructions>) conceptModelDetails.getInstructions();
should be
List<Instructions> operations = conceptModelDetails.getInstructions();
in the spring controller.