What would be considered an acceptable way of dealing with returning a record from a DB with the following 3 potential outcomes:
- Db connection works, finds a user and returns a populated user object
- Db connection works, doesn't find a user, returns a new user object
- Db connection/query fails...
I'm for the most part aiming for design by contract:
class Scratch {
public User getUser(int id) {
try {
// Prepare SQL Query
PreparedStatement s = this.connection.prepareStatement(
"select * from get_user(?)"
);
// Provide SQL Parameters
s.setInt(1, id);
// Run our SQL
ResultSet rs = s.executeQuery();
rs.next();
// Extract data into Entity
User user = User.createFromDatabase(rs);
rs.close();
return user;
} catch(Exception e) {
e.printStackTrace();
}
return new User();
}
}
In the situation that the DB connection or query fails it's a little less obvious what I should do, I have a few options:
- Return a new user object, because our method has agreed to return a user
- Pro: This sticks to designing by contracts
- Con: This makes it look like the user doesn't exist.
- Return null since it didn't actually get a user.
- Pro: This is totally clear that a user was not found
- Con: Requires a null check
- Throw an exception further up the chain.
- Pro: Makes it explicitly clear the operation wasn't achieved
- Con: Does not attempt to rectify issues where they happen
I'm leaning towards handling Exceptions as it avoids the billion-dollar mistake, and also maintains design by contract in normal situations.
However I'd like to know if there are any obvious pitfalls or if this is a familiar situation with a well established pattern to resolve it.