JPA EmptyResultDataAccessException handling - java

I'm writing a simple library API for a college project. I have a database with books, each with it's own ID. I'm using Spring Boot to make the service. I have a BookRepository which extends JpaRepository<Book, Long>, and a service implementation.
#Service
public class BookServiceImpl implements BookService{
#Autowired
private BookRepository bookRepository;
#Async
#Override
public void delete (Long id){
bookRepository.delete(id);
}
}
Later on, a REST controller handles the request:
#RestController
public class BookServiceController{
#Autowired
private BookService bookService;
#RequestMapping(value="books/{id}", method = RequestMethod.DELETE)
public ResponseEntity<Book> deleteBook (#PathVariable("id") Long id){
bookService.delete(id);
return new ResponseEntity<Book>(HttpStatus.OK);
}
}
Now, if I were to delete a Book which is not in the database, for example with an ID of 123, I'd get a EmptyResultDataAccessException thrown.
My question is, how and where do I handle the exception, and how do I avoid casting a NullPointerException that way?
Thanks in advance.

In the case of a DELETE operation, you aren't really returning an entity anymore; you're simply confirming that the resource is gone. As DELETE is idempotent (you could delete the record multiple times), you can either return the same status code regardless of whether the record exists or return a 404 if the record isn't found. You can also simplify the handler method:
#DeleteMapping("/books/{id}")
#ResponseStatus(HttpStatus.NO_CONTENT) // because you deleted it
public void deleteBook(#PathVariable Long id) {
try {
bookService.delete(id); // or just use the repository directly
} catch (EmptyResultDataAccessException ex) {
// either do nothing to return a 204, or
throw new NotFoundException();
}
}
where you have an exception that indicates the status:
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {}
It's plausible that EmptyResultDataAccessException should already be annotated with a status of NOT_FOUND; this is a potential enhancement request.

The most elegant way to avoid the EmptyResultDataAccessException is to define an specific method in your Repository interface to delete by your id field.
Let's supppose that your id field is named bookId:
public interface BookRepository extends JpaRepository<Book, Long> {
Long deleteByBookId(Long bookId);
}
In this way the exception won't be throwed.
I haven't tested the solution if your id field is simply called id.

Related

Spring boot JPA saved entities not getting when retrieve with EntityManager native query in a #Async method

I have a service class like this.
#Service
public class ServiceAImpl implements ServiceA {
#Autowired
private ServiceARepository repo;
#Autowired
private Publisher publisher;
#Transactional
public String save(Model model) {
//entity list creation code goes here
List<EntityA> entityList = repo.save(entityList);
repo.flush();
...
//savedIdList is comma separated list of entityList EntityA.id
publisher.publish(savedIdList);
return responseObject.toString();
}
}
When controller call to this service its create the Entity list and save. After that its call to publish method in another class with the saved ids. This save method annotated with #Transactional.
#Service
public class Publisher {
#Autowired
private AsyncPublisher asyPublisher;
#Autowired
PublishedDataRepository repo;
public void publish(String savedIdList) throws Exception {
savePublishData(..., savedIdList);
}
#Transactional
private void savePublishData(..., String savedIdList) throws Exception {
SearchPublishedData publishData = new SearchPublishedData();
...
publishData.setIdList(savedIdList);
publishData = repo.saveAndFlush(publishData);
asyPublisher.callPublisher(..., savedIdList, publishData.getId());
}
}
In publisher class its save a record to the publisher table and again it call to the async publisher class. In this class there is a method with #Async and its implemented with ThreadPoolTaskExecutor. In this async method what it going to do is get the previously saved data from its ids using EntityManager native query.
This is a sample java class code. Actually in this native query there are few tables join with this previously saved table and getting the data.
#Service
public class AsyncPublisher {
#PersistenceContext
private EntityManager entityManager;
#Async("threadPoolTaskExecutor") //This is created as ThreadPoolTaskExecutor
public void callPublisher(..., String ids, Long savedId) {
try {
String query = getQuery(ids);
List<Object[]> results = entityManager.createNativeQuery(query).getResultList();
... ///Other codes goes here, but results is empty
} catch (Exception e) {
logg error
}
}
private String getQuery(String ids) throws Exception {
StringBuilder query = new StringBuilder();
query.append("SELECT * FROM EntityA_table WHERE id IN (").append(ids).append(" ) ");
//This is a sample query, actually in this query there are 2 or more tables joined
return query.toString();
}
}
My problem is when I retrieve data from EntityManager native query time to time its not getting the data. But when I check the database with those ids those data available in database.
Anyone can give me the reason for this.
I think this saving block is annotated with #Transactional and it going to commit data to the Database at the end of the method execution and but before it save to the database EntityManager native query execution happens in another thread and its execute before the data commit. Is this correct? If not please explain someone what is happening here.
Also is there any way to avoid this data not retrieve scenario?
Thank you.

How to utilize the #ResponseStatus annotation when handling the ResourceNotFoundException

I have the following service class:
#Service
public class CitiesServiceImpl implements CitiesService {
#Autowired
private CitiesRepository citiesRepository;
#Override
public City getCityById(Integer cityId) {
return citiesRepository.findById(cityId)
.orElseThrow(ResourceNotFoundException::new);
}
}
and it is used in my controller:
#RestController
#RequestMapping("/cities")
public class CitiesController {
#Autowired
private CitiesService citiesService;
#GetMapping("/{cityId}")
public City readCity(#PathVariable Integer cityId) {
return citiesService.getCityById(cityId);
}
#ExceptionHandler(ResourceNotFoundException.class)
String handleResourceNotFound(Exception e) {
return e.getMessage();
}
}
So when the readCity is called with a nonexistent cityID, the ResourceNotFoundException will be thrown and then handled by the handleResourceNotFound exception handler.
However, when the ResouceNotFoundException is handled, the status code in the response is still 202, i.e. OK. Seems that #ResponseStatus annotation in the ResourceNotFoundException is not utilized at run time. This can be fixed by adding the #ResponseStatus(value=HttpStatus.NOT_FOUND) to the method handleResourceNotFound, but such code is duplicate as the #ResponseStatus annotation is already in the ResourceNotFoundException.
Question: How to utilize the ResponseStatus annotation of the ResourceNotFoundException instead of adding duplicate code?
Remove that handleResourceNotFound and let framework handle that for you, or return proper Response from handleResourceNotFound method.
By declaring such handler you are saying that you will handle such case, thus framework is backing off.

What are the best practices to handler or throw exceptions in a Spring Boot application?

I've developed a rest api using Spring Boot. In one of my service methods, I throw a ServletException, in case a specific user is not found. I am wondering if that is the best way to do that, I mean, is that the right layer to thrown the exception?
Creating a custom exception type is a better idea than using ServletException.
In order to handle an exception you can use #ControllerAdvice.
First create custom exception type:
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Assuming that your controller and service look more or less like this:
#RestController
#RequestMapping("users")
class UserController {
private final UserService userService;
UserController(UserService userService) {
this.userService = userService;
}
#GetMapping
List<String> users() {
return userService.getUsers();
}
}
#Service
class UserService {
List<String> getUsers() {
// ...
throw new UserNotFoundException("User not found");
}
}
You can handle you UserNotFoundException using #ControllerAdvice
#ControllerAdvice
class CustomExceptionHandler {
#ExceptionHandler({UserNotFoundException.class})
public ResponseEntity<Object> handleUserNotFoundException(UserNotFoundException exception) {
return new ResponseEntity<>(exception.getMessage(), HttpStatus.NOT_FOUND);
}
}
One of best way or what I do is,
Check data / parameters for valid data( e.g Null check sometime manually using if statement).
Checking data / parameters for size (like file size)
checking data or parameters for valid range also data type, do typecasting if not in valid type (like String, Long, Integer etc).
Raise a message and return to that API, if the data / parameters are not valid before system raise an exception
I am assuming you are looking to catch all exceptions occured inside your application. Spring-Boot provides a Global Exception Handler to catch all the exceptions gracefully and return response according to the the specific Exception.
It gives you flexibility to change the status codes, response data, headers accordingly. Few useful links to implement this feature is -
1.) Dzone
2.) Spring Boot Tutorial
Throwing exception in your #Service is okay. ServletException is not super meaningful. What I would suggest is to create your own Exception class extending RuntimeException and throw it.
So you would end up with something like that:
A Controller that only calls a service method (better not to have any
logic here)
#RestController
#RequestMapping("/users")
public class UserController {
#Autowired
private UserService userService;
#GetMapping("/{id}")
public User getUserById(#PathVariable("id") Long id) {
return userService.getById(id);
}
}
A Service class that calls DAO class (extending JPARepository)
#Service
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Override
public User getById(Long id) {
return userDAO.findById(id).orElseThrow(() -> new UserNotFoundException("No user with id = " + id + " found."));
}
}
DAO:
#Repository
public interface UserDAO extends JpaRepository<User, Long> {
}
note: it returns Optional<Object> which is very convinient.
And finally your own Exception class.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message);
}
}
Note: #ResponseStatus - it is going to return HTTP Status code 404 on throwing this exception.
This is imho a very clean and good way to develop your rest api.
Also take a look here: How to get spesific error instead of Internal Service Error . I answered a question providing information you might find useful

When and how to instantiate a Spring Bean in my Rest Api

First of all, I'm a relative noob to Spring Boot, so keep that in mind.
I've got a REST api in which I'm trying to minimize database calls for the same object and I've determined that using a Spring Bean scoped to the Request is what I want. Assuming that is correct, here is what I'm trying to do:
1) Controller takes in a validated PhotoImportCommandDto command
PhotoCommandController
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> importPhoto(#Valid #RequestBody PhotoImportCommandDto command){
...
}
2) PhotoImportCommandDto is validated. Note the custom #UserExistsConstraint which validates that the user exists in the database by calling a service method.
PhotoImportCommandDto
#Component
public class PhotoImportCommandDto extends BaseCommand {
#NotNull(message = "userId must not be null!")
#UserExistsConstraint
private Long userId;
...
}
What I would like to do is somehow set a Spring Bean of the user that is validated in the #UserExistsConstraint and reference it in various methods that might be called throughout a Http request, but I'm not really sure how to do that. Since I've never really created my own Spring Beans, I don't know how to proceed. I've read various guides like this, but am still lost in how to implement it in my code.
Any help/examples would be much appreciated.
You can use the #Bean annotation.
#Configuration
public class MyConfiguration {
#Bean({"validUser"})
public User validUser() {
User user;
//instantiate user either from DB or anywhere else
return user;
}
then you can obtain the validUser.
#Component
public class PhotoImportCommandDto extends BaseCommand {
#Autowired
#Qualifier("validUser")
private User validUser;
...
}
I don't really know how to make annotations in Java. Anyway, in Spring, checking where the User exists in the DataBase or not is one line of code:
userRepository.findOne(user) == null
That is accomplished by the Spring Data JPA project:
Create a JPA Entity User.
Set the spring.datasource.url and login/password in the
resources/application.properties.
Create this interface:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface UserRepository extends CrudRepository<User, Long> {
}
Note, Spring implements it behind the scences.
Inject this interface into your RestController (or any other Spring bean):
private UserRepository userRepository ;
**constructor**(UserRepository ur){
userRepository = ur;
}
Note, a Spring Bean is any class annotated #Component (this includes stereotype annotations like Controller, Repository - just look up the contents of an annotation, it may use #Component internally) or returned from a method which is annotated #Bean (can only be on the Component or Configuration class). A Component is injected by searching the classpath, Bean is injected more naturally.
Also note, injecting is specifying #Autowired annotation on field or constructor, on a factory method, or on a setter. The documentation recommends that you inject required dependencies into constructor and non-required into the setter.
Also note, if you're injecting into a constructor and it is clean by the arguments, you may omit #Autowired annotation, Spring will figure it out.
Call its method findOne.
So, you can do one of the following:
Inject the userRepository into the #RestController constructor (as shown above). I would do that.
Inject the userRepository into the #Service (internally #Component) class that will do this sorts of thing for you. Maybe you can play with it to create an annotation.
p.s. Use #PostMapping instead of #RequestMapping(method = RequestMethod.POST)
p.p.s. If ever in doubt, go to the official documentation page and just press CTRL-F: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/ Note the current word, that will always take you to the latest version.
p.p.p.s Each Spring project has its own .io webpage as well as quick Get Started Guides where you can quickly see the sample project with explanations expecting you to know nothing.
Hope that helps! :)
Don't forget to mark the answer as accepted if you wish
Using Jose's input, I took a bit of a different route.
Here's what I did:
I created a ValidatedUser class:
#RequestScope
#Component
public class ValidatedUser {
private UserEntity user;
public UserEntity getUser() {
return user;
}
public void setUser(UserEntity user) {
this.user = user;
}
}
and I also created a wrapper class HttpRequestScopeConfig to capture all variables to use over the course of an Http Request to the api.
#Component
public class HttpRequestScopeConfig {
#Autowired
private ValidatedUser validatedUser;
...
public UserEntity getValidatedUser() {
return validatedUser.getUser();
}
public void setValidatedUser(UserEntity validatedUser) {
this.validatedUser.setUser(validatedUser);
}
...
}
In my UserExistsConstraintValidator (which is the impl of #UserExistsConstraint, I set the validatedUser in the httpRequestScopeConfig:
public class UserExistsConstraintValidator implements ConstraintValidator<UserExistsConstraint, Long> {
//private Log log = LogFactory.getLog(EmailExistsConstraintValidator.class);
#Autowired
private UserCommandService svc;
#Autowired
private HttpRequestScopeConfig httpRequestScope;
#Override
public void initialize(UserExistsConstraint userId) {
}
#Override
public boolean isValid(Long userIdField, ConstraintValidatorContext context) {
try {
UserEntity user = svc.findUserOfAnyStatus((Long) userIdField);
if (user != null) {
httpRequestScope.setValidatedUser(user);
return true;
}
} catch (Exception e) {
//log.error(e);
}
return false;
}
}
Now, I can access these variables throughout the rest of my service layers by autowiring HttpRequestScopeConfig where necessary.

HibernateTemplate not getting the object when called through TaskExecutor

I have a web service DocGenerationServiceImpl that inserts (for every format) a record in the table using DocRepository and object representing the record as DocFileDO. In the for-loop, I can get the id of the record that was created in the table. For each record, I will call the executor's execute method where DocGenTask will search for the record given the id. However, for example, there are 3 formats, the DocGenTask is able to get only the last record. The first 2 it cannot find. Although it's using hibernateTemplate. Can please advise?
#RestfulService
#Controller
#RequestMapping("/docs")
public class DocGenerationServiceImpl {
#Autowired
private TaskExecutor taskExecutor;
#Autowired
private DocRepository docRepository;
#RequestMapping(value = "/generate", method = RequestMethod.POST)
#ResponseBody
public String generatedDocFile(DOCParam param) {
for(String format : param.getFormatList()) {
DocFileDO docFileDO = new DocFileDO();
...
docRepository.saveDocFile(docFileDO);
log.debug("docFileDO id = " + docFileDO.getId());
DocGenTask task = new DocGenTask(docFileDO.getId());
task.setDocRepository(docRepository);
taskExecutor.execute(task);
}
}
}
#Repository
public class DocRepository {
#Autowired
private HibernateTemplate hibernateTemplate;
public DocFileDO saveDocFile(DocFileDO docFile) {
hibernateTemplate.save(docFile);
hibernateTemplate.flush();
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return hibernateTemplate.get(DocFileDO.class, docFileId);
}
}
public class DocGenTask implements Runnable {
public void run() {
generate();
}
private void generate() {
DocFileDO docFileObj = docRepository.getDocFile(docFileId);
}
}
A couple of things
Don't use HibernateTemplate it should be considered deprecated as of Hibernate 3.0.1 (which was released somewhere in 2006). Use the SessionFactory directly and use the getCurrentSession() method to get a hibernate Session to operate on.
You don't have transactions setup (judging from the snippets), to work with a databse you need proper transaction setup.
Your controller is doing much, all of this should be inside a service.
The first refactor your repository
#Repository
public class DocRepository {
#Autowired
private SessionFactory sf;
public DocFileDO saveDocFile(DocFileDO docFile) {
Session session = sf.getCurrentSession();
session.save(docFile);
return docFile;
}
public DocFileDO getDocFile(Long docFileId) {
return sf.getCurrentSession().get(DocFileDO.class, docFileId);
}
}
Now your code will probably fail due to improper transaction setup. Add #Transactional to all the methods (or class) that need a transaction (like the saveDocFile method).
As mentioned you probably should move the code found in the controller to a service. The controller should be nothing more then a thin integration layer converting from the web to an internal representation of something and then kick off a service/business method somewhere. This service-/business-method is also your transactional unit-of-work it either all succeeds or all fails.

Categories

Resources