I am trying to update a resource in DB via #PutMapping (org.springframework.web.bind.annotation.PutMapping) by calling a stored procedure via (java.sql.PreparedStatement.executeUpdate()). This step is working absolutely fine and I am getting below integer response from DB:
1 - 1 row updated
0 - No row updated
I want to send the response back to the user on the basis of integer returned from the DB, for that I am returning ResponseEntity<UpdateResponseVO> from the rest controller, where UpdateResponseVO is normal POJO class:
public class UpdateResponseVO {
private String responseCode;
private String message;
}
I am framing the ResponseEntity on the basis of below condition:
UpdateResponseVO apiResponse = new UpdateResponseVO();
if (row > 0) {
apiResponse.setResponseCode("200");
apiResponse.setMessage("Updated Successfully.");
return new ResponseEntity<UpdateResponseVO>(apiResponse, HttpStatus.OK);
}
else {
apiResponse.setResponseCode(???);
apiResponse.setMessage("No row updated.");
return new ResponseEntity<UpdateResponseVO>(apiResponse, ???);
}
Now my question is what should be my response code when 0 row gets updated (this condition arises when we are not aware of the current value to be updated in the DB and we give the same value. For e.g in DB already student name is "abc" and we want to update it to "abc" again. Hence row updated will be 0) and what HttpStatus I should send along with the ResponseEntity.
I am not sure where you are handling exception when receiving wrong data, Recommended way to use #Validated and validate your request DTO receiving in your controller body. Use #ControllerAdvice for globally handling exception.
Now, Specifically you should return HttpStatus.FORBIDDEN i.e 403 as this is data validation error. In your case you check if row==0 simple ,In general if there is any processing error then respond back with 500. I personally prefer generic response in Success/Error case with HttpStatus.OK with the use of ResponseEntity<?> where you can return any object in response.
Related
I'm trying to reactively fetch data from external API using two methods from some Service class.
I'm new to reactive and Spring in general, so it could be a very obvious mistake but I just can't find it
These are the two methods:
public Mono<SomeClass> get(int value) {
return webClient.get()
.uri("/" + value)
.retrieve()
.onRawStatus(HttpStatus.CONFLICT::equals, clientResponse -> {
return Mono.error(new SomeException1("Some message", clientResponse.rawStatusCode()));
})
.onRawStatus(HttpStatus.NOT_FOUND::equals, clientResponse -> {
return requestGeneration(value)
.flatMap(res -> Mono.error(new SomeException1("Some message", clientResponse.rawStatusCode())));
})
.bodyToMono(SomeClass.class)
.retryWhen(Retry.backoff(5, Duration.ofSeconds(8))
.filter(throwable -> throwable instanceof SomeException1));
}
private Mono<Void> requestGeneration(int value) {
return webClient.post()
.uri("/" + value)
.retrieve()
.onRawStatus(HttpStatus.BAD_REQUEST::equals, clientResponse -> {
return Mono.error(new SomeException2("Wrong value", clientResponse.rawStatusCode()));
})
.bodyToMono(Void.class);
}
Baasically what I'm trying to achieve is:
first GET from http://api.examplepage.com/{value}
if that API returns HTTP404 it means I need to first call POST to the same URL, because the data is not yet generated
the second function does the POST call and returns Mono<Void> because it is just HTTP200 or HTTP400 on bad generation seed (i don't need to process the response)
first function (GET call) could also return HTTP429 which means the data is generating right now, so I need to call again after some time period (5-300 seconds) and check if data has been generated already
then after some time it results in HTTP200 with generated data which I want to map to SomeClass and then return mapped data in controller below
#PostMapping("/follow/{value}")
public Mono<ResponseEntity<?>> someFunction(#PathVariable int value) {
return Mono.just(ResponseEntity.ok(service.get(value)));
}
all the code I posted is very simplified to the issues I'm struggling with and doesn't contain some things I think are not important in this question
and now the actual question:
it doesn't actually make the call? i really don't know what is happening
program doesn't enter onRawStatus, even if i change it to onStatus 2xx or whatever other httpstatus and log inside i see nothing as if it doesn't even enter the chains
when i manually call with postman it seems like the program calls have never been made because the GET call returns 404 (the program didn't request to generate data)
the controller only returns "scanAvailable": true, when i expect it to return mapped SomeClass json
// edit
i changed the code to be a full chain as suggested and it didn't solve the problem. all the status are still unreachable (code inside any onStatus nor onRawStatus never executes)
I have several custom queries in an interface that extends JpaRepository. The interface is analogous to the below (note: the below is just for illustration, and may have errors, but don't concern yourself with that).
public interface MyRepo extends JpaRepository<SMark, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(id, col_1, col_2) " +
"values(:col_1, :col_2))", nativeQuery = true)
int insertRecord(#Param("col_1") String col_1, #Param("col_2") String col_2);
So, my issue is that I am finding it difficult to do anything useful with the int return type for anything other than a successful query (which will return a 1). Is there a way to do anything useful with the return other than sending the value as part of the response? In other words, if the response is not a 1, and an exception is not thrown, can the non-1 response be translated into something more informative to the user?
For example, I am currently doing the following, which I would like to improve upon if I was a confident about the not-1 status:
if(status == 1) {
StatusResponse statusResponse = new StatusResponse("Successful Delete ", null);
return new ResponseEntity<>(statusResponse, HttpStatus.OK);
}
else {
StatusResponse statusResponse = new StatusResponse("Delete not successful (lacking details from response) ", null);
return new ResponseEntity<>(statusResponse, HttpStatus.NOT_ACCEPTABLE);
}
Grateful for any response. Thanks!
I would not recommend the approach of using return type as valid operation or not. I would prefer having database level constraint like unique constraint, check constraint or trigger check for insert/update/delete.
Nevertheless, we can use default method inside interface and throw database exception and in Spring you can configure ExceptionHandler to wrap the exception and return with some valid error code ?
public interface MyRepo extends JpaRepository<SMark, String> {
#Transactional
#Modifying
#Query(value = "INSERT INTO my_table(id, col_1, col_2) " +
"values(:col_1, :col_2))", nativeQuery = true)
int insertRecord(#Param("col_1") String col_1, #Param("col_2") String col_2);
default void insert(String col1, String col2) {
if (insertRecord(col1, col2) != 1) {
throw new DataIntegrityViolationException("Unable to perform DML operation.");
}
}
}
Since the return value of this methods indicates the number of modified objects (in your can it can only be 0 or 1) I would just use it to translate it into a more understandable response.
The simplest case is a REST Api:
When the Method returns 1 the POST/PUT/PATCH call was successful (if there was no other error) and you would return 201 Created and maybe a little more like a Location header.
And when it returns 0 it means no object got modified and because it's an insert this shouldn't happen and therefore you would return 500 InternalServerError
But I would say in your case this is redundant because as I said it's an insert , you only have those two return options and I guess if something doesn't work during the insert you already get an exception by spring boot and therefore when you called the method without getting an error I would say it was successful.
But ofcourse you can double check to be sure and maybe even use it for tests to enforce the expected behavior or something else.
I have a simple post method that accepts temperature and checks is the temperature is greater than or equals to 37.4 and returns a response as cleared or not cleared.
#PostMapping(value = "/temperature")
public ResponseEntity<?> Temperature(#Valid #RequestBody Activation activation) throws UnsupportedEncodingException {
Temperature temperature = new Temperature();
temperature.setStatus(activation.getStatus());
temperature.setTemperature(activation.getTemp());
if (db_activation.getCode().equals(code)) {
temperature.setSource("VENUE");
temperatureRepository.save(temperature);
return ResponseEntity.ok(activation.getTemp() < 37.4 ? "Cleared" : "Not cleared");
}
}
how can I insert the response to Status column (which can be cleared or not cleared) to the database and will it be a post request or a put? Please suggest how to do that
To set the status, you can use something like below,
temperature.setStatus(activation.getTemp() < 37.4 ? "Cleared" : "Not cleared")
If you want to create the resource, POST should be the method, for updates, PUT should be used.
Choosing the right request type is just a matter of convention. Technically you could perform any operation with any type of request. By convention: POST sends some fresh data tonthe server, PUT sends an update of the server's data. So if it's a persisted data, POST would be "add" and PUT would be "update".
You could do it in the same method, like so:
if (activation.getTemp()< 37.4) {
temperature.setStatus("Cleared");
} else {
temperature.setStatus("Not cleared");
}
instead of this line:
temperature.setStatus(activation.getStatus());
I have a simple controller method that calls a spring data repository and returns a few objects.
#RequestMapping(value="/api/learnitemlists", method=RequestMethod.GET, produces=MediaType.APPLICATION_JSON_UTF8_VALUE)
public ResponseEntity<Iterable<LearnItemList>> getLearnItemLists(#RequestParam(value="fromLanguages") Optional<List<String>> fromLanguages,
#RequestParam(value="toLanguage") Optional<String> toLanguage,
#RequestParam("pagenumber") Integer pageNumber,
#RequestParam("pagesize") Integer pageSize) {
LOGGER.debug("start of learnItemLists call");
Page<LearnItemList> lists;
lists = learnItemListRepositoryCustom.findBasedOnLanguage(fromLanguages,toLanguage,new PageRequest(pageNumber,pageSize));
HttpHeaders headers = new HttpHeaders();
headers.add("X-total-count", Long.toString(lists.getTotalElements()));
headers.add("Access-Control-Expose-Headers", "X-total-count");
ResponseEntity<Iterable<LearnItemList>> result = new ResponseEntity<Iterable<LearnItemList>>(lists,headers,HttpStatus.OK);
LOGGER.debug("end of learnItemLists call");
return result;
}
I logged the beginning and the end of the method call:
22:06:11.914 - 22:06:12.541
So the actual retrieval of objects from the database is well under 1 second. However, the full request took about 2.68 when trying in a browser (integration tests show similar performance).
I can't help but think that something is off. Can serialization into JSON (I'm using Jackson) take this long? The whole JSON response is about 1 kb...
So is this normal (I doubt it), and if not, what steps should I take to find out the cause?
The following code is part of a large project.
Overview is that I am trying to access a database using Spring MVC. I want to update a field based on request and send a response as to what values the database sent back.
Code:
#Override
#Transactional
public EmployeeResponse update(EmployeeRequest employeeRequest) {
Employee emp = new Employee();
UUID empId = UUID.fromString(employeeRequest.getId());
Employee foundEmployee = employeeRepository.findOne(empId);
if (foundEmployee != null) {
foundEmployee.setAddress(employeeRequest.getAddress());
// similarly set 4 fields of foundEmployee
emp = employeeRepository.save(foundEmployee);
}
EmployeeResponse response = new EmployeeResponse();
response.setAddress(emp.getAddress());
// similarly set 4 fields of response
return response;
}
I found that there was no new Employee() for foundEmployee as there is for emp.
I am not sure but I think this'll cause exceptions.
Am I correct?
Also, please tell me what exception I should throw when foundEmployee is null.
Additional info - this is what the help shows:
org.springframework.data.repository.CrudRepository
public T findOne(ID id)
Retrieves an entity by its id.
Parameters:
id - must not be null.
Returns:
the entity with the given id or null if none found
Throws:
IllegalArgumentException - if id is null
In the line
Employee foundEmployee = employeeRepository.findOne(empId);
we can presume that EmployeeRepository.findOne() will return an instance of Employee. This will not cause a compiler error, and if an exception happens at runtime, it would be inside findOne().
With regard to what you should do in the event of a null foundEmployee, it is really a design decision you will have to make. One option would be to have the method return null to let the consumer know that the EmployeeRequest which was passed in had a serious problem with it.
Another option would be to create your own Exception and then throw it in the case of null foundEmployee.
Update:
In light of that you need to pass something back to your UI, another option would be to create an empty EmployeeReponse object and return that:
EmployeeResponse response = new EmployeeResponse();
response.setAddress(null);
response.setName(null);
Make sure that your framework can marshall null values into something which is user friendly, for example an empty string for all fields.