Page 1 of 4 123 ... LastLast
Results 1 to 10 of 33

Thread: Need advice on exception handling approach

  1. #1
    Join Date
    Jul 2005
    Posts
    5

    Default 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. #2
    Join Date
    Dec 2005
    Location
    U-241
    Posts
    237

    Default

    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.");
            }
        }
    Spring, it's a wonderful thing...

  3. #3
    Join Date
    Jul 2005
    Posts
    5

    Default

    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...

  4. #4
    Join Date
    Dec 2005
    Location
    U-241
    Posts
    237

    Default

    Quote 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!!!
    Quote 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.
    Quote Originally Posted by sk
    Actually, I am trying to find the most preferable approach...
    When you will, please, keep me posted
    Spring, it's a wonderful thing...

  5. #5
    Join Date
    Jul 2005
    Posts
    5

    Default

    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

  6. #6
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    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.

  7. #7
    Join Date
    Dec 2005
    Location
    U-241
    Posts
    237

    Default

    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
    Spring, it's a wonderful thing...

  8. #8
    Join Date
    Dec 2005
    Location
    Argentina
    Posts
    73

    Default

    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

  9. #9

    Default

    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)

  10. #10
    Join Date
    Aug 2004
    Posts
    1,905

    Default

    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.
    Colin Yates
    SpringSource - http://www.springsource.com - Spring Training, Consulting, and Support - "From the Source"
    Please read http://www.springframework.org/documentation
    Co-Author of Expert Spring MVC + Web Flow.

Posting Permissions

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