With the wonders of Springs DAO model we never *have* to deal with exceptions. However, any intellegent programmer should. So my question is when and where in the flow of things? So I will post some code to go along with my question.
Ok, now with all that over. What should be throwing exceptions? For example, I think it would be a good idea to throw an exception when you do an insert. But when you get the list of users I think you should just check that the list > 0. But on the other hand, say the app is running and the database just goes down, so when you try and query the list, wouldn't it be a good idea to catch an exception that the Service is down (ServiceException)?
I am starter myself and I have similar app as you have. Unfortunatedly I can't test at the moment. Its all a bit broken, because of not so glorious caching experiments. It should throw unchecked execeptions. What about: Just drive the database down and watch out for what comes down the layers And violating some constraints in your db-model. Are you interested in caching experiments during weekend?
Axel [ April 28, 2005: Message edited by: Axel Janssen ]
Joined: Jan 08, 2001
last action this night: From api-docs of spring: org.springframework.orm.ibatis Class SqlMapClientTemplate:
Helper class that simplifies data access via the SqlMapClient API of iBATIS SQL Maps, and converts checked SQLExceptions into unchecked DataAccessExceptions, following the org.springframework.dao exception hierarchy. Uses the same SQLExceptionTranslator mechanism as JdbcTemplate.
Actually I've read in first Rod Johnson book where he was critizising JDBC Exception. For being checked, because when db is down or constraint has been violated, what can client do in catch-clause to make call against db work? Also better support for SQLState and SQLCode values in exception.
I would catch the unchecked exception in service layer, provide user a message he understands and log the exception in log file.
Good night. [ April 28, 2005: Message edited by: Axel Janssen ]
Look a little closer at Gregg's DAO implementation; he's already using the template class. The question still remains: whether a checked SQLException or an unchecked DataAccessException, what do you do with it?
My first maxim in catching and handling an exception is "Don't do it if you can't really handle it." The second is "If it's already unchecked, don't convert it or wrap it in another unchecked exception unless you can add something truly worthwhile."
For example, there are an endless number of non-system related events that can cause your application to throw an unchecked exception or error: your app runs out of memory (OutOfMemoryError) or a bug slips into production (IllegalArgumentException, NullPointerException, ArrayIndexOutOfBoundsException), etc. Regardless, you'll likely handle them exactly as you'd handle a DataAccessException: redirect to a generic error page telling the user to try again or come back on Tuesday.
Given that you'll handle them the same way, why bother wrapping DAException inside ServiceException? What does that add? The servlet container, web framework, or event loop is going to catch all those with a single catch clause and take the same action each time. True, you might be able to determine from a DAException that the problem is likely to be temporary, but that's still an option with a raw DAException versus a ServiceException.
Long story short: let it propagate up the call chain for the controller to catch and handle unless it's part of your application's business logic. For example, maybe your website has the ultra-consumer-friendly policy that "If the payment system goes down, all customer orders are free." In that case you'd have to catch and parse the DAException in your business logic.
Now I've got a question for you. What's the difference between UserService and UserDao? UserService's API looks just like UserDao's. I inherited a similar design a couple years ago, only it took it to a new level: UserManager -> UserStore -> UserDao -> entity beans. The signatures and exception names were different at each layer, but the pattern they formed was identical. This required a lot of redundant code as each layer had to repackage objects and translate exceptions for the other layers.
Worse still, all of the business logic was in the Struts Action classes. This proved to be quite inflexible and difficult to maintain or extend. However, I've seen this pattern more than a few times.
My belief is that the business logic layer (UserService in your example) should use the DAO layer but not mirror it. It should provide an expressive business-oriented API rather than a form of CRUD that differs only slightly from the DAO's. Granted, it's still common in today's applications to present the user with a set of fields, let them change some, and shoot them off to the database. Yes, you'll still have some of those, but I feel they run counter to OO design and should be avoided when possible.
In any case, I was just curious to know your thinking. Of course, I'm extrapolating from a very small example and could easily be mistaken. Whatever your UserService layer looks like, I highly recommend against putting the business logic in the presentation layer. You'll duplicate a lot of code and paint yourself into a corner. You'll also double the probability that your next assignment will be to create a thin yet comprehensive web services layer.
A good book on this topic is Domain Driven Design. Hmm, that reminds me ... I got side-tracked and didn't finish the book. Guess I'll have to pop it up the stack a few levels.
Thanks. Your thread was very helpful. As far as why am I doing it this way. Well, because this is how Spring In Action suggested doing it. I often wondered myself why I needed a Service interface that looked identical to my DAO interface with the exception of a few minor details.
My belief is that the business logic layer (UserService in your example) should use the DAO layer but not mirror it. It should provide an expressive business-oriented API rather than a form of CRUD that differs only slightly from the DAO's.
I see your point. None of my servies really do anything beyong CRUD operations. I am always just reading and writing stuff to the database. So I guess I could almost get rid of that layer all together??
Here is how I currently have this mapped in Spring
So with my given classes and interfaces, how would this change? [ April 30, 2005: Message edited by: Gregg Bolinger ]
Joined: Aug 07, 2003
Most applications really only need a CRUD interface, whether it's the optimal form for the application or that we tend to stick with what we know is anyone's guess. Even though my latest application has a few areas with fairly complex logic, most of the data access is read-modify-write at the object level.
However, if you only need a CRUD layer, don't give in to temptation to collapse it into the DAO layer and have your servlets call your DAOs. There are two reasons off the top of my head.
First, the DAOs shouldn't be doing transaction management. This allows multiple DAOs to be employed in a single transaction. Your business logic should be responsible for transaction demarcation. And definitely don't put the demarcation in the servlets.
The second reason is a little more practical: the moment you commit to coding directly against the DAOs, your application's requirements will change and you'll be forced to live with a mess or redo it.
For these reasons, when we ported our application from session and entity beans to Spring and Hibernate, I took the opportunity to collapse the unnecessary layers I mentioned above but kept the Manager and DAO layers distinct. The API for all of the Manager classes remained unchanged, but everything below them was gutted and rewritten from scratch.
I would have loved to ditch the Manager layer since it is basically a CRUD layer, but I know we'll start migrating the business logic from the servlets down into the Manager layer. So I'd say in your case if a CRUD layer satisfies your requirements, go for it. I think it will save you headaches later.
subject: Spring DAO: When and Where to catch/throw exceptions