Mar 4th, 2009, 09:35 PM
It seems to me that in this particular example you are not going to clarify anything by catching and re-throwing. Think about it. If you do nothing, just allow the RTE from the data tier (I assume you are using Spring data access support that will wrap SQLExceptions into RTEs) propagate all the way to your top-level resolver, log it and display the generic error page, your log file will contain the stack trace that will tell you just exactly what your new RTE would have told: a constraint violation has occured during a call to "createUser()". The transaction will be rolled back anyway, if you properly configure your transaction manager. An additional "clarifying" RTE is not going to add any more useful info. So, don't bother. Also, I would not bother extracting the DAO call into a private transactional method, unless there's a lot more going on in your public createUser method. Just define createUser as @Transactional.
Originally Posted by ramoq
Now, if you do know what a constraint violation really means in this particular case, e.g. "duplicate user", then you indeed might want to translate that exception into a meaningful DuplicateUserException (if you really want to signal this condition with an exception) and use a resolver to listen to DuplicateUserException specifically and map it to a different view, if necessary. And you probably would want to do that inside the DAO, without exposing this to the service method, since your data access implementation is supposed to be abstracted from the business logic in the service. What if your data store changes - hypothetically - and you start retrieving the same data from a file system instead. No more constraint violation exceptions, but your service has the code that assumes they occur. Put DA-specific logic inside DAOs and keep it there.
Also, you might want to have some utility class somewhere in your common area - with generic validation methods such as "ValidationUtils.notNull(Object o, String classname)", and the likes of that. You can write an aspect that you can apply to all your public service methods that would check all method arguments for not being null - before executing the method. This will not affect the code of your service methods, since the logic will be implemented once and externalized in an aspect. The aspect would throw some InvalidArgumentException with the detail message containing the name of the class whose instance was not passed (null) properly, etc.
So, in most cases, you don't need to have any try/catches in your service or DAO methods at all - unless some 3rd party or JDK methods throw checked exceptions that you will need to catch and wrap into meaningful RTEs. In fact, most of my service and DAOs are squeaky clean, you'd be hard pressed to find a try/catch in them. And may I say so, by the time my applications are in QA, I don't miss any errors, and my log files read like a book.