Page 3 of 6 FirstFirst 12345 ... LastLast
Results 21 to 30 of 55

Thread: Service Layer Exceptions

  1. #21
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by ramoq View Post
    - In regards to RTE's thrown at transaction commit times, ie. the db unique constraint is violated etc.

    - Since generally my service methods are marked with @Transactional, should I proxy these calls and catch the RTE's then wrap them and return back a valid msg to the user?

    ie.
    Code:
    public User createUser(String email, String name){
         try{
               createUserTransaction(email, name);
        }catch(/* runtime error */){
         //throw back meaningful RTE wrapping the exception
        }
    
    }
    
    @Transactional
    private User createUserTransaction(String email, String name){
       //construct domain object, dao/persist logic
    }
    I can't see another way to catch these RTE's and return back a meaningful RTE to the client. (And even if I did an explicit check to see if the email is unique, it's not guaranteed until the transaction commits)
    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.

    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.

    Good luck.

  2. #22
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Quote Originally Posted by constv View Post
    It seems to me that in this particular example you are not going to clarify anything by catching and re-throwing.
    ...
    Hi Constantine, all what you said is true, but you somewhat missed a point, sorry. The question essentially was "what to do with delayed exceptions?".

    There are some situations when exceptions are thrown not at the moment of the method call but in some (somewhat indeterministic as it may be changed by configuration external to your code) point later, probable long after return from your DAO methods. I have explained some such situations in my previous post.

    You may be interested to look (if you have not done it before) on chapter 10.10 of Hibernate Reference. And note that while in case of Hibernate you may force immediate flush, it may impact performance catastrophically (up to 2 orders of magnitude, i.e. 100 times).

    If it is caused by deferred (i.e. enforced only on commit) constraints in DB then you can do absolutely nothing about it (assuming that there were valid reasons to create those constraints in deferred mode - and sometimes such reasons exist).

    So the question is - what is a best solution in this case (aside resolvers, they are applicable not always) ? I'm very interested in your opinion.

    Regards,
    Oleksandr

  3. #23
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    The question essentially was "what to do with delayed exceptions?".
    I must have misunderstood. I thought we were talking about simply handling exceptions in the service layer during the API execution, with service API being transactional - meaning that if anything fails within the demarcated transaction - in the DAO or outside the DAO but within the service method that wraps the DAO call, the transaction would be rolled back. Assuming, of course, that the database does not do any explicit commits until the framework's transaction management triggers the commit.

    Let's see if I understand you correctly... Are you are talking about database-related errors that may happen due to whatever external factors - after the service API successfully returns and the transaction is successfully committed? Like during Hibernate's flushing that normally happens behind the scenes and performed by Hibernate at its own will unless we force it? Is this what ramoq was asking?

    Hmm... I agree with you that forced flushing every time you execute a query is expensive and most likely would be a huge overkill. It seems to me that dealing with such conditions should - normally - be trusted to the framework itself (Hibernate, in this case) and if such abnormal condition ever happens, there's little you can do on the application/service API side. In other words, I am using Hibernate because it abstracts certain things that I supposedly don't need to worry about. If that framework all of a sudden does something out of whack and screws up the integrity of the whole system, that's the framework's malfunction. That error will eventually manifest itself as a critical database exception and should go straight to the top handler for critical/fatal errors. However, I tend to think that the likelihood of such conditions hugely depends on the quality of the data access/database design, in general. Perhaps your system should not be designed so that it heavily relies on huge volumes of potentially stale cached objects, etc. Don't you agree? Stuff like that should not be happening in well designed systems, and if it does it should be treated as a marginal critical condition and looked into from the stand point of a possibility of design tweaking rather than error handling. That's my take on it. (I know, I have seen some horrendous Hibernate implementations - usually in cases where ORM was totally inappropriate, in the first place. As you have said before, people sometimes misuse good technologies. I think ORM should be used when a very clear and straightforward mapping between your object model and database schema can be achieved without making one ridiculously complex to fit the other. But that's a different topic. )

  4. #24
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Quote Originally Posted by constv View Post
    ...
    Let's see if I understand you correctly... Are you are talking about database-related errors that may happen due to whatever external factors - after the service API successfully returns and the transaction is successfully committed?
    Not exactly - such error occur after service API return in the process of commit (i.e. transaction would not be successfully committed).

    Like during Hibernate's flushing that normally happens behind the scenes and performed by Hibernate at its own will unless we force it? Is this what ramoq was asking?
    More or less - he has not explicitly mentioned Hibernate. And Hibernate (and ORM in general) is not required to obtain such kind of behavior. You may meet it even with old plain JDBC, for example, if you have in the database something like following (Oracle):

    Code:
    CREATE TABLE games
      (scores NUMBER, CONSTRAINT unq_num UNIQUE (scores)
       INITIALLY DEFERRED DEFERRABLE);
    Such constraint is checked not on insert/update but only on commit.
    Hmm... I agree with you that forced flushing every time you execute a query is expensive and most likely would be a huge overkill.
    100-fold performance degradation due to excessive flushes I have witnessed first hand.

    It seems to me that dealing with such conditions should - normally - be trusted to the framework itself (Hibernate, in this case) and if such abnormal condition ever happens, there's little you can do on the application/service API side.
    First of all framework may not be involved, secondly, while I rather can not recover from this condition (while it depends on business requirements) I may - and rather should - provide meaningful message to the client. DUPLICATED_KEY does not do as it may be duplicated user id, duplicated SSN, and so on. There should be some piece of code that is aware of DAO implementation details. And it is better if it is independent from the way how service API was called, e.g. handler exception resolver would not always do.

    So, from my point of view it should be some wrapper (manual or via AOP) that calls exception resolution service, which in turn calls exception resolver built-in into DAO layer.

    Regards,
    Oleksandr

    In other words, I am using Hibernate because it abstracts certain things that I supposedly don't need to worry about. If that framework all of a sudden does something out of whack and screws up the integrity of the whole system, that's the framework's malfunction. That error will eventually manifest itself as a critical database exception and should go straight to the top handler for critical/fatal errors. However, I tend to think that the likelihood of such conditions hugely depends on the quality of the data access/database design, in general. Perhaps your system should not be designed so that it heavily relies on huge volumes of potentially stale cached objects, etc. Don't you agree? Stuff like that should not be happening in well designed systems, and if it does it should be treated as a marginal critical condition and looked into from the stand point of a possibility of design tweaking rather than error handling. That's my take on it. (I know, I have seen some horrendous Hibernate implementations - usually in cases where ORM was totally inappropriate, in the first place. As you have said before, people sometimes misuse good technologies. I think ORM should be used when a very clear and straightforward mapping between your object model and database schema can be achieved without making one ridiculously complex to fit the other. But that's a different topic. )[/QUOTE]

  5. #25
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by al0 View Post
    First of all framework may not be involved, secondly, while I rather can not recover from this condition (while it depends on business requirements) I may - and rather should - provide meaningful message to the client. DUPLICATED_KEY does not do as it may be duplicated user id, duplicated SSN, and so on. There should be some piece of code that is aware of DAO implementation details. And it is better if it is independent from the way how service API was called, e.g. handler exception resolver would not always do.

    So, from my point of view it should be some wrapper (manual or via AOP) that calls exception resolution service, which in turn calls exception resolver built-in into DAO layer.
    Ok, I see your point. Yes, I agree. And I have stated in one of the earlier posts that if some particular data access condition may - and should - be interpreted to assist in diagnostics, it should be done on the DAO layer. If that is specific to a particular DAO call, it may be a try/catch around the dao method, if it is more general and applies to many DAO calls, I would use an aspect. The purpose of that aspect or try/catch would be just to wrap into something meaningful and re-throw, and then still have the front-end resolver catch it.

  6. #26
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Quote Originally Posted by constv View Post
    then still have the front-end resolver catch it.
    If that front-end exist (which is not always a case).

  7. #27
    Join Date
    Nov 2006
    Location
    Boston, MA
    Posts
    303

    Default

    Quote Originally Posted by al0 View Post
    If that front-end exist (which is not always a case).
    well, d'oh! I think ramoq was originally talking about his web application, specifically... Or, I may be dreaming.

  8. #28
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    No, you were not dreaming - he has mentioned controller, so it is almost for sure Web application.
    Quote Originally Posted by constv View Post
    well, d'oh! I think ramoq was originally talking about his web application, specifically... Or, I may be dreaming.

  9. #29
    Join Date
    Mar 2009
    Posts
    15

    Default

    I haven't fully read through your replies. so before I ask more questions (i want to read it over in detail first) i'll state some answers to yoru questions.

    - Yes, it's a web app
    - I'm using JPA via hibernate.
    - No deferred constraints, i'm using MySQL.

  10. #30
    Join Date
    Mar 2009
    Posts
    15

    Default

    - So yes, essentially I DO NOT want to recover from this error. No doubt there.

    - However, I would like to prompt the caller of this service that the email used already exists with a EmailExistsException (RTE of course ) or something along those lines.

    - This would then allow the caller to handle this in a specific fashion. Ie. prompt the user to re-enter the password with a specific msg. "please enter a different email, this one is in use.."

    - I guess flush() is out of the question.

    - However, since I was keeping my DAO layer limited to very basic CRUD operations( save(), update(), delete() findByEmail() etc). This was the reason I was opting for the hacky solution I posted. Unless you guys think this is a bad design for DAO layers (containing very basic crud operations only)

    - Should have a createUser() method in my DAO and wrap that with a try/catch in my service? Obviously the createUser DAO call would run in it's own transaction.

    - what would a solution look like in terms of what I listed? Avoiding aspects if possible.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •