New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add option so that CrudRepository.findOne throws an unchecked exception if entity is not found [DATAJPA-118] #544
Comments
Vincent Demeester commented I think the "option" (via jpa:repository tag) should be more generic : It should enable/disable the throwing of Persistence Exception (like NotFoundException, …) |
Marcel Stör commented I first meant to whole-heartedly support this feature request but I'm not so sure anymore... |
Michael Andrews commented I originally voted up (after all, I ended up here looking to see if I was doing something wrong, and hoping there was an option to throw such an exception automatically). But after some thought - It should be left up to the exported Service Layer or controller layer to throw the exception. (IMO). |
Sergey Parhomenko commented Agreed that extra findOneWithException(id) method would be better (although the name is not ideal). I think the logic of throwing this exception belongs more in the repository layer. |
Oliver Drotbohm commented Some thoughts here:
try {
Person person = repository.findOne(id);
// more code in case of success
catch (EmptyResultDataAccessException e) {
// error handling
} over Person person = repository.findOne(id);
if (person == null) {
// error handling
}
// more code in case of success If you really want to throw an exception in this case (e.g. to automatically indicate a 404 in a web framework) you'll need to catch and wrap the original exception anyway. So I currently fail to see how the exception way of resolving the issue creates any benefit except a second exception instance to be created. From a pure design point of view I'd argue the case of not finding an entity for a given id is not really an exception but a standard case. So I feel like abusing the concept of an exception to implement control flow to some degree here. However, I'm definitely in favor to rethink the issue in case we find a few use cases that explain benefits of the exception approach. Still, please keep in mind that changing the behavior of the existing method is close to impossible as we cannot break existing clients. The same would apply for an addition of a new method as we'd have to implement the method by all implementations across supported stores |
Richard Eckart de Castilho commented null values tend do propagate through an application, making it harder later to trace down the origin on the null. Exceptions fail fast and can be handled further up the call-chain - possibly even display a reasonable message in the UI. However, if the "read" method throws an exception when an entity is not available, there should be some other "exists" method to check for the presence. Otherwise, odd code like this might pop up:
10 cents from Twitter |
Vivek Kondur commented I agree with Oliver & Richard. null values could propagate and would be harder to debug. An exception is preferred, which lets the Application developer to handle the scenario in a desired manner |
Oliver Drotbohm commented Richard Eckart de Castilho: This feels a bit like trading one way working around the lack of a proper Your code snippet could be written as follows: if (!repository.exists(id)) {
return repository.save(new Person(…));
} else {
return repository.findOne(id)
} That causes an additional query in every case. As the you want to return the existing object in case it can be found anyway we usually see code like this, if you really want to create a default instance in case the lookup fails (although I'd argue it's more likely you want to implement dedicated error behavior as indicated in my previous post, e.g. assemble a dedicated more semantic exception or the like): Person person = repository.findOne(id);
return person == null ? repository.save(new Person(…)) : person; |
Oliver Drotbohm commented Vivek Kondur: Agreeing with me and voting for the exception doesn't work together :). The JavaDoc clearly states that the method might return |
Adam Thody commented A separate method that throws an exception would be pretty ugly API design. The API shouldn't be leaking its implementation details. I agree with Oliver, there are many valid reasons why a query for a particular Now, returning A |
Vivek Kondur commented
However, I would have preferred an exception (EmptyResultDataAccessException) thrown from the findOne(id) method |
Stevo Slavić commented +1 for returning null, and using exceptions for exceptional situations only. Searching and not finding in this case IMO is not an exceptional situation, like it is e.g. persisting entity when (FK) referenced entity is not found in repository. When Java 8 comes, replace returning null with java.util.Optional |
Brian Clozel commented +1 for null and Optional when it is available. |
Romain Manni-Bucau commented Null or exception are equivalent. JPA uses exception, sprin data chose null which is fine in more cases |
Ricardo Gladwell commented I would recommend making the default behavior to throw an exception. Returning null by default its a problem because developers may forget to handle the null, and it may propagate elsewhere. An exception will fail fast |
Ranie Jade Ramiso commented +1 for returning null. I have to agree with
|
Ricardo Gladwell commented
|
Ranie Jade Ramiso commented
|
Thomas Darimont commented Hello, if one really need this functionality now, one could (IMHO) implement this easily with an Aspect that advises / wraps Best regards, |
Bogdan Calmac commented A compelling use case for throwing an exception is a @RequestMapping(value="/{id}", method=RequestMethod.GET)
public Spittle spittleById(@PathVariable Long id) {
return spittleRepository.findOne(id);
}
@ExceptionHandler(SpittleNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public @ResponseBody Error spittleNotFound(SpittleNotFoundException e) {
return new Error(4, "Spittle [" + e.getSpittleId() + "] not found");
} |
Bogdan Calmac commented For my use case, I've solved the problem by extending CrudRepository and adding a default method findOneX(). Now it's up to the caller to use the return null or throw exception behavior. For a @NoRepositoryBean
public interface CrudRepositoryX<T, ID extends Serializable> extends CrudRepository<T, ID> {
default T findOneX(ID id) throws ObjectRetrievalFailureException {
T result = findOne(id);
if (result == null) throw new ObjectRetrievalFailureException("Entity " + id + " not found", null);
return result;
}
} |
Armando Garcia commented +1 for throwing an exception. My two cents: This method doesn't search by some random property, it searches by an specific Primary Key, imo there should not be a "valid" scenario where a user searches for an unexistant ID. If you actually have an ID you want to search for is because the object was already created previously, and not finding it should be treated as an exception, not part of the regular flow. That's why we have the 404 error anyway, if you are making up the route, then you should throw an error, not just an empty response, if you are following a url that previously existed, then that url is considered a broken url, and you should not be given broken urls to the user, you must consider it an exception in the backend because the user shouldn't have access to the broken link in the first place imo, the root cause would be in the client sending the user to it |
Oliver Drotbohm commented I'd argue the exact opposite: the fact, that there's a 404 status code which is not special at all, it indicates that the absence of a resource is not an exceptional case but one explicitly integrated into the protocol. Just imagine a client trying to access the resource after it has been deleted by another client. That URI is not "invalid", it just points to a resource, that doesn't exist (anymore). Also, I think focussing on the HTTP case is shortsighted as it's just one scenario. Other clients using the repository might have to resort to catch an exception to handle the missing entity case which is suboptimal, too, as creating exceptions is expensive and it ultimately leads the user to misuse them for flow control. Generally speaking, I think both solutions (returning |
Armando Garcia commented Hi Oliver, thanks for taking the time to review my comment. Actually I do see something very special about the 404 code, the fact that its in the 4XX range means its a Client Error. If you were to search for a resource , say a customer, by its name, you'd return an empty list or a null object, but a 2xx code, the search was successfully ran, however the query returned an empty result. But if you look for it by its ID, you return a 404, which means its an invalid request. I might have been valid once when the resource existed, but not anymore, hence the uri does become invalid, just as your access card to your company becomes invalid once you're no longer working for them, the fact that it existed at some point doesn't mean its going to be valid forever. As for the "Other clients using the repository might have to resort to catch an exception" part, I wonder what's the purpose of the exists method if not this precisely, not having to deal with the exception. Also exceptions mean exceptional cases, that's what the catch is for, the fact that they exist doesn't mean you can't do anything for them, it means, its an exceptional case, and you might or might not be able to do something about it, if you can, you catch it and do something about it, If you can't, you let it bubble up. Its not about HTTP specifically, but its an example of how this is usually treated, another example its JPA which does throw an exception |
Oliver Drotbohm commented Two things: no, a 404 is not something exceptional. It just another status code. A URI does not become "invalid" just because the resource pointed to is absent and the server returns 404. That's how HTTP works from the ground up. There's nothing special at all about that. It might be from your application point of view. That doesn't make it special on the protocol level. As the name suggests, the purpose of the exists method is to check whether something exists or not. That check can be used for arbitrary things ("If exists, do this, else that."). It's in place to avoid the object to be materialized in the first place because it might not even be needed. I don't quite get what that has to do with whether I stated that I consider returning |
Anders Norgaard commented +1 from me for the option to have not found exception. Maybe I am biased but mapping to a REST-ish interface (eg. via Spring RestController) is just so common that I think the option would be used quite a lot |
Jens Schauder commented With the changed API in Spring Data 2.x this issue seems solved, and either way Olli made his opinion pretty clear I think |
(can I comment on closed issues?) Optional or null based results force repository user to check something. To give an example: Entity findByName(name); Will force user of repository to repeat Optional<Entity> findByName(name); Will force user of repository to repeat Now, advantage of exception based apis is possibility to create exception handlers working in an aspect-like manner (and just not care about empty results). handleDbException(BaseDbException ex) {
// logic like reporting it to error tracker, maybe returning specific error code, maybe mapping to some other exception
} Now by the above I don't necessarily mean controller-level (infrastructure) exception handler - you might want something like this in your domain (making a decision to couple with aspect library or being very careful with using some base class). While having an option to have unchecked exception as a result of repository call would make things simpler (especially with some convention like Please note that using this approach we can free domain logic from empty-result handling concern. Imho it can tremendously improve readability retaining design security that |
Adrian opened DATAJPA-118 and commented
For a clean design it would be great if I could set an option (maybe on the jpa:repository tag) that will throw a javax.persistence.EntityNotFoundException (or some other unchecked exception) instead of returning null if CrudRepository.findOne does not find the entity.
Issue Links:
("is duplicated by")
14 votes, 25 watchers
The text was updated successfully, but these errors were encountered: