Announcement Announcement Module
Collapse
No announcement yet.
Need advice on exception handling approach Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Need advice on exception handling approach

    Hi all,

    Suppose we are implementing the code for "register a new user to Spring forum". And you need to guarantee that username is unique...

    (There is a service layer, a dao layer, and a view layer. Using declarative transaction management.)

    Here is what i thought:

    hmm, i can put a constraint check in database, let Spring throw DataIntegrityViolationException and catch it in the service layer and
    wrap it within a more specific and meaningful exception and throw the new exception.

    Here is the service layer code:
    Code:
        @Transactional
        public void save(User User) {
            try {
                dao.save(user);
            } catch (DataIntegrityViolationException ex) {
                throw new BusinessException("Duplicate username.");
            }
        }
    You might guess what actually happens in this situation; i can't catch the exception, because dao.save(user) would not really save to the database
    (hence the constraint viloation does not manifest) until transaction commits (when the save method exits).

    Any suggestion? What do you think i should do?

  • #2
    Have you tried to flush to db manually thus forcing immediate insertion to db? Something like this:
    Code:
    @Transactional
        public void save(User User) {
            try {
                dao.save(user);
            // or EntityManager  in EJB3  
                Session.flush();    
        } catch (DataIntegrityViolationException ex) {
                throw new BusinessException("Duplicate username.");
            }
        }

    Comment


    • #3
      Hi,

      It solves the problem, but I wonder if it is the right way of handling this kind of situations?

      I don't recall seeing manual flush in common sample codes. And in the API, it says that "Only invoke flush for selective eager flushing, for example when JDBC code needs to see certain changes within the same transaction. Else, it's preferable to rely on auto-flushing at transaction completion"

      Hence, I thought maybe the way I am handling the situation is wrong by design.

      (Besides, if I will have to flush, wouldn't dao layer be a more appropriate place?)

      Actually, I am trying to find the most preferable approach...

      Comment


      • #4
        Originally posted by sk
        I don't recall seeing manual flush in common sample codes.
        (Besides, if I will have to flush, wouldn't dao layer be a more appropriate place?)
        Well, It's not the Spring you are dealing with in this scenario. You are dealing with Hibernate.
        Here is some example from Chapter 8 of the Hibernate in Action by Christian Bauer and Gavin King (the father of Hibernate)
        Code:
        Triggers and ORM are often a problematic combination. It's difficult to synchronize the effect of a trigger with the in-memory representation of the data.
        
        Item item = new Item();
        ...
        HibernateUtil.beginTransaction();
        Session session = HibernateUtil.getSession();
        session.save(item);
        session.flush(); // Force the INSERT to occur
        session.refresh(item); // Reload the object with a SELECT
        System.out.println( item.getCreated() );
        HibernateUtil.commitTransaction();
        HibernateUtil.closeSession();
        Most problems involving triggers may be solved this way, using an explicit flush() to force immediate execution of the trigger, perhaps followed by a call to refresh() to retrieve the result of the trigger.
        Triggers and ORM are often a problematic combination. SIC!!! SIC!!!
        Originally posted by sk
        (Besides, if I will have to flush, wouldn't dao layer be a more appropriate place?)
        I guess, this is a perfectly valid argument. I gave just an illustration. right? This manual flushing stuff is not application business logic but rather persistence logic, i.e. the way ORM deals with triggers. Therefore it should belong to DAO implementation layer.
        Originally posted by sk
        Actually, I am trying to find the most preferable approach...
        When you will, please, keep me posted

        Comment


        • #5
          Thx Arno. %100 agree with you

          I was just curious, why i never saw flush in sample codes. So, I thought maybe i needed a radical design change(like catching exception in view layer)

          Ok, i made a deeper check and saw some trustable sample codes, handling the same situation with flush and I am reliefed

          Comment


          • #6
            Maybe you can add an aspect to the service that runs outside the transactional aspect/proxy. This way you can catch the exception and convert it.
            However, I don't think that will work well for more complex cases where many things can fail, also there is the complexity cost of setting up an aspect for every conversion. Documentation for such method (the service method) should state that there's a conversion mechanism in the middle, otherwise the exception appears to be generated out of nowhere.

            Anyway, this is just a brain dump. I'm not convinced that this option is actually better.

            Federico.

            Comment


            • #7
              There are scenarios out there where you cannot use manual flush. For instance, long (application, user) persistence transaction or batch job utilizing facilities of the stateful session bean. Like this example of batch insertion job
              Code:
              @Stateful
              @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
              public class InitBean implements Init {
                  @PersistenceContext(unitName = "titan", type = PersistenceContextType.EXTENDED)
                  private EntityManager manager;
              
                  @Spring(jndiName = "spring-pojo", bean = "statefulTravelService")
                  private TravelService travelService;
              
                  @PostConstruct
                  public void postConstruct() {
                      Assert.notNull(travelService, "Travel service is NULL!!!");
                      travelService.setManager(manager);
                  }
              
                  @Remove
                  @TransactionAttribute(TransactionAttributeType.REQUIRED)
                  public void flush() {
                      // syncrhonize with database;
                  }
              
                  public void initAirports(int index) {
                      logger.debug("initAirports() index " + index);
              
                      int size = getNumber(Airport.class);
                      if (size > 0) {
                          return;
                      }
                      String[] airports = new String[] { "Denver", "Hoover", "Chicago", "New York",
                              "San Francisco", "Cleveland", "Toronto", "Berlin",
                              "Washington", "Madison", "San Antonio", "Venice", "Rome",
                              "Istanbul", "Berlin", "Ottawa", "Vancoover", "Alamo", "London",
                              "Paris" };
              
                      List<Airport> lst = new ArrayList<Airport>();
              
                      for (int i = 0; i < index; i++) {
                          Airport port = new Airport();
                          int k = Util.createRandomInt(airports.length - 1);
                          String airport = airports[k];
                          port.setName(airport);
                          port.setAirportCode(airport.substring(0, 3).toUpperCase() + "_" + (i + 1));
                          lst.add(port);
                      }
                      travelService.createAirports(lst);
                  }
                 
                  private int getNumber(Class clazz) {
                      return travelService.getNumberOfObjects(clazz);
                  }
              
              }
              You cannot put flush in your dao in this environment. So aspect catching and converting exception looks like a viable trick to me.

              Cheers,
              Arno

              Comment


              • #8
                I just read an article about the new aspect stuff in spring 2. Aspect autoproxying eases the deployment of aspects considerably...

                Here is the link: http://www.infoq.com/articles/Simpli...nterprise-Apps

                Federico

                Comment


                • #9
                  We created an afther-throwing advice wich is set on business methods.
                  Code:
                  <aop:aspect id="dataAccessToBusinessException" ref="dataAccessToBusinessExceptionAdvisor" >
                  			
                  			<aop:after-throwing 
                  				pointcut-ref="businessMethods"
                  				method="afterThrowingData"
                  				throwing="dataException"/>
                  				
                  
                  		</aop:aspect>
                  in this class I then convert / handle the DataAccessException or whatever exception my businessmethod has thrown. (I also use an adjusted sql-error-codes with my own exceptions and extra data in them)

                  Comment


                  • #10
                    I would suggest that all of this is somewhat indicative of poor validation

                    Does your validator check that the username is unique? Of course, there is *still* a tiny tiny window where the username is unqiue at the time of validation but not when you actually persist it, but it is probably a couple of ms.

                    Time and time again people seem to be resistant to putting DB checks in their validator, but why That is the point of validation.

                    Rant over.

                    Comment


                    • #11
                      Colin,
                      Why should I duplicate functionality of existing database? Why Java validation is better then validation performed by database? I prefer let the trigger do its job and then handle its response. Otherwise I have to maintain two codebases - scripts (stored procedures, triggers) and Java to keep them in sync.

                      Comment


                      • #12
                        I would say that the answer to that is performance. Maybe it is just premature optimization, but I understand if people wants to avoid doing the same check twice (code and db).

                        Comment


                        • #13
                          So you don't do *any* checks in code? You don't check that the mandatory fields are set, that the min/max lengths aren't exceeded etc.?

                          If you do, then you have answered your own question

                          Comment


                          • #14
                            I do not mix validation into a single mumbo-jumbo. I separate syntax validation (upper case, characters, max-min length which can be performed even by HMTL) from semantic validation. If part of this semantic validation is performed by database due to some complex rules set by the database schema (and, incidentally, maintained by other guys - DBA), I do not want to duplicate those rules in my code. I rather rely on db trigger and handle response. Those DBA guys changed something in their trigger code, OK my code will react to their change with a properly handled exception.

                            Comment


                            • #15
                              I do not want to duplicate those rules in my code
                              Huh! so what the reason for having a middle tier (application layer)? I wonder if you can get done with having bunch of stored procedures and triggers doing job for ya'. Why do you need high profile language like java to do that? or why do you even need a middle tier to your application? Hummmmm, so If your application is to respond to some third party application and not your database than other third party have to make sure the data provided to them is not dirty data. Is that what you are telling us? Or even if your company decided to do a database upgrade or change you want to assume that DBA's would do their job right and your application should work as long as they do their job right. Hummm so whats the point having a saperate exception handling architecture in your application? Geez man I don't even understand why are you using Object Oriented technologies or a pattern oriented architecture or even a high profile framework like spring.
                              Last edited by tatvamasi; Aug 25th, 2006, 09:14 AM.

                              Comment

                              Working...
                              X