5 Replies Latest reply on Jun 27, 2007 12:45 PM by gavin.king

    Handling database exceptions

    shakenbrain

      When persisting an entity at the end of a conversation, how can one handle a database exception, like a constraint violation, without invalidating the session? Here's the the basic scenario:

      1. Start a conversation (flushMode=MANUAL); display edit page
      2. Call a method to persist the edited object; calls entityManager.flush()
      3. A unique constraint is violated; EntityExistsException is thrown
      4. Exeption is caught and a FacesMessage created
      5. Control returns to the edit page
      6. Edit page accesses database to fetch entities for a <s:selectItems> tag
      7. Exception thrown because transaction no longer valid

      Gavin states somewhere on this forum that it's a good idea to check the database before persisting an object to see if a constraint would be violated. I'd prefer to keep my constraint logic in the database- it means a lot less coding in action methods. For example, I don't have to worry about whether the object getting flushed is new or not. Also, verifying a constraint won't be violated before persisting does not guarantee that the constraint won't be violated in the database (ie, another session inserts an identical object at the same time). Validating constraints in the control layer just isn't robust enough.

      So, is there a way to keep the current session intact when a database exception is thrown? Section 5.3.1 in the reference manual seems to indicate that the answer is 'no'. If that's true, that seems to be a pretty major shortcoming (of EJB3, I think).

        • 1. Re: Handling database exceptions
          gavin.king

          You can't. If a flush fails and an exception propagates out of the database, there is no practical way for the ORM to figure out what the current state of the database is, and resynchronize the in-memory state to that.

          Check your constraint first.

          • 2. Re: Handling database exceptions
            shakenbrain

            Understood. Gavin, thanks for your response.

            Getting back to validating then, it appears that using a @Validator class causes the session to go invalid when it throws a ValidatorException, but only on the whole form submit (it works if you do a partial form submit using ajax4jsf on just that one field, using <a:support>). Here's a validator class that does nothing but throw the exception:

            @Validator
            @Name("prefixValidator")
            public class PrefixValidator extends BaseValidator {
            
             @In
             EntityManager entityManager;
            
             // @Transactional
             public void validate(FacesContext context, UIComponent component, Object value)
             throws ValidatorException {
            
             throw new ValidatorException(makeMessage("Prefix is being used already."));
            
             }
            }


            and the relevent xhtml:

            <s:decorate id="prefixDecorator" template="/decorateField.xhtml">
             <ui:define name="label">Prefix</ui:define>
             <h:inputText id="prefix" size="4" value="#{selectedOrganization.prefix}" required="true" disabled="#{!organizationAdmin.new}">
             <f:validator validatorId="prefixValidator"/>
             </h:inputText>
             </s:decorate>


            That should work right? Is this a bug? I haven't tried this in 2.0 Beta yet, I'll try to do that today.


            • 3. Re: Handling database exceptions
              gavin.king

              It will only happen if (a) the EM gets enlisted in a tx and (b) the tx gets rolled back.

              • 4. Re: Handling database exceptions
                shakenbrain

                ValidatorException being thrown shouldn't cause the transaction to roll back, right?

                • 5. Re: Handling database exceptions
                  gavin.king

                  It did until this latest release (2.0beta)