Correct handling of return data [closed]
Asked Answered
C

4

10

I have a question related to correct handling of returns of the DAO library I'm writing for one project. This library probably is going to be used by another people, and I want to do it correctly. How I should deal with return statements of the functions of my DAO?

Example 1 I have function to getCustomer which should return String. In case a query doesn't return any result should I return null, an empty string or throw some kind of exception?

Example 2 I have a function, getCutomerList, which returns a value of type ArrayList<String>. In case a query doesn't return any result should I return null, an empty ArrayList or throw some exception?

Example 3 Some SQL exception was detected, what should I do, throw exception or do try..catch of the block where it can occur?

What is the "good" practice or "best" practice to apply in my case?

Corpuscle answered 7/2, 2011 at 14:54 Comment(0)
C
10

It seems your library is doing database-like calls. If that is the case, then I would do exactly what is implemented by the JPA 2 specification.

What I mean by that is, look at the find() method in JPA API and return exactly what they are doing there.

    /**
     * Find by primary key.
     * @param entityClass
     * @param primaryKey
     * @return the found entity instance or null
     *    if the entity does not exist
     * @throws IllegalStateException if this EntityManager has been closed.
     * @throws IllegalArgumentException if the first argument does
     *    not denote an entity type or the second
     *    argument is not a valid type for that
     *    entity's primary key
     */
    public <T> T find(Class<T> entityClass, Object primaryKey);

You see here in find, which I think is similar to your getCustomer() method, it will return null if none is found, and only throwing IllegalArgumentException if the argument is invalid.

If the find() method is not close to what you want with getCustomer() you should implement the same behavior as getSingleResult():

    /**
     * Execute a SELECT query that returns a single result.
     * @return the result
     * @throws EntityNotFoundException if there is no result
     * @throws NonUniqueResultException if more than one result
     * @throws IllegalStateException if called for a Java 
     *    Persistence query language UPDATE or DELETE statement
     */
    public Object getSingleResult();

Which will throw EntityNotFoundException if no result is found, NonUniqueResultException if multiple instances are found or IllegalStateException if the SQL is wrong.

You have to decide which behavior is most suitable for you.

The same goes for getResultList():

/**
 * Execute a SELECT query and return the query results
 * as a List.
 * @return a list of the results
 * @throws IllegalStateException if called for a Java 
 *    Persistence query language UPDATE or DELETE statement
 */   
public List getResultList();

getResultList() will return null if none is found and only throwing exception if the SQL is illegal.

By following this behaviour, you are being consistent, and your users will get a feeling of knowing how the library is.


An alternate behavior is to return an empty collection instead of null. This is how Google Guava have implemented their API, and which is really the preferred why. However, I like consistency, and still think you should implement the library as close to the standard as possible.


Resources

Joshua Bloch made a video explaining how to design a good API and why it matters.

Coopt answered 7/2, 2011 at 14:59 Comment(4)
Note that the javadoc is not precisely identical to the jpa 2 api, however the behavior is similarCoopt
Ok, seems very convincing throwing special exceptions in case of elements not found and the killing argument :P which is users being comfortable with api. Thanks a lot for your help :)Corpuscle
Also read the resources link I gave you, and remember to mark the answer as accepted if you wish to do soCoopt
One quick note on a typo: for getSingleResult, your explanation lists the EntityNotFoundException twice. The second one should be NonUniqueResultException. Still, an amazing answer. +1.Goring
H
2
  1. null. But method getCustomer() should return Customer. If it returns String it should be probably called getCustomerName() or getCustomerId()
  2. empty list
  3. throw exception. Probably wrap it with application-layer exception.
Hetero answered 7/2, 2011 at 15:1 Comment(2)
+1 for mentioning that getCustomer should return an object. Otherwise the null could be ambiguous. Was the customer name null, or was the customer not found?Crenation
Ok, my function is more getCustomerName then getCustomer, obviously that I would return Customer instead of String ;) but thanks anyways pointing to that lapseCorpuscle
H
1

Example 1: Since nothing is retrieved, null should be returned. Alternatively, Null-Object pattern can be a choice.

Example 2: Prefer empty ArrayList to null. See "Effective Java" Item 43: Return empty arrays or collections, not nulls

Example 3: Translate the SQLException to higher Exception and throw it. See "Effective Java" Item 61: Throw exceptions appropriate to the abstract

Halflength answered 7/2, 2011 at 15:1 Comment(1)
Interesting approach on using Null-Object pattern, need to see this more closely. Thanks for indicating the reference to Effective JavaCorpuscle
I
1

Since it's your API, any approach is good as long as you are consistent and make sure that it is documented properly.

For 1 & 2: Just take note though, that returning nulls will force client code to keep on performing checks like the following:

result = yourAPICall();
if(result != null){
   // do something
}

Which is why I prefer returning empty objects or collections

For 3: That will depend on your APIs design. But first, never throw low level exceptions up the call stack. You should wrap them in an custom exception class designed for your API so that your client code will only need to catch your API exceptions rather than a variety of lower level ones (SQLException, IOException etc...)

Second, you must decide if there is any benefit for the exception to be thrown in the first place. Throwing an exception allows client code the ability to customize how it wants to the handle problems encountered by its API dependencies. But throwing it also prohibits you as the API designer from designing internal contingencies that will allow your code to perhaps recover from the problem (makes your API less robust)

Insular answered 18/2, 2011 at 11:1 Comment(1)
For 3 I have created customized exception like ElemenetNotFoundException,plus some of the exceptions, related to the connectivity, are being handled by api. Thanks for advice anyways :-)Corpuscle

© 2022 - 2024 — McMap. All rights reserved.