Announcement Announcement Module
No announcement yet.
Is JPA exception translation too simple? Can you chain other translators after it? Page Title Module
Move Remove Collapse
Conversation Detail Module
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is JPA exception translation too simple? Can you chain other translators after it?


    I am rather dissappointed by the way JPA exception translation works. For example: when violating a unique contraint (i.e. @Column(unique = true)), I get a generic "JpaSystemException" when I would have hoped for a "DataIntegrityViolationException".

    I had a look at the Spring 3.0.5 code to check under the hood and found that it tries to perform JPA exception translation using the method jpaconvertJpaAccessExceptionIfPossible() from class
    org.springframework.orm.EntityManagerFactoryUtils. In this method, all it does for "generic" persistence exception is:

    	// If we have another kind of PersistenceException, throw it.
    	if (ex instanceof PersistenceException) {
    		return new JpaSystemException((PersistenceException) ex);
    To be honest, I was expecting some more effort in exception translation. At the very least a unique constraint violation should end up being a DataIntegrityViolationException.

    In this specific case, one could argue that it's also the fault of the JPA spec (Hibernate is throwing a "PersistenceException" which is the only JPA exception that would make sense; the actual cause inside is wrapped within the generic PersistenceException using the hibernate-specific "org.hibernate.exception.ConstraintViolationExcept ion").

    I seriously doubt the usefulness of this approach.

    1) Does anyone practically rely on this to handle errors?
    2) Is it possible to configure a "hibernate-specific" exception translator to be invoked in the case of a PersistenceException; I mean an exception translator that will unwrap the PersistenceException.getCause() and try to translate the actual Hibernate error?

    P.S. I'm using JPA (implemented by Hibernate, with MySQL as the database).

  • #2
    Then there must be something missing/wrong with your setup. The case you describe is the last effort to translate the exception. First the HibernateJpaDialect should check the exception and the cause if either of those is a HibernateException it delegates to the SessionFactoryUtils to translate the exception. So if you use spring to configure the EntityManager there should also be something else to translate the exception.


    • #3
      Ok, so what could be wrong?

      So, here's my setup. Let me know if you see something missing.

      I'm using AspectJ with compile-time-weaving. The JpaExceptionTranslatorAspect automatically kicks in to perform the translation. For example, here is a stack trace of a domain entity that threw the exception:

      Caused by: org.springframework.orm.jpa.JpaSystemException: org.hibernate.exception.ConstraintViolationException: could not insert: [model.Account]; nested exception is javax.persistence.PersistenceException: org.hibernate.exception.ConstraintViolationException: could not insert: [model.Account]
      	at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(
      	at org.springframework.orm.jpa.aspectj.JpaExceptionTranslatorAspect.ajc$afterThrowing$org_springframework_orm_jpa_aspectj_JpaExceptionTranslatorAspect$1$18a1ac9(JpaExceptionTranslatorAspect.aj:15)
      	at model.Account.submitForRegistration(
      	at webui.registration.RegisterForm.onSubmit(
      	at org.apache.wicket.markup.html.form.Form$10.component(
      	at org.apache.wicket.markup.html.form.Form$10.component(
      	at org.apache.wicket.util.visit.Visits.visitPostOrderHelper(
      	at org.apache.wicket.util.visit.Visits.visitPostOrder(
      	at org.apache.wicket.markup.html.form.Form.delegateSubmit(
      	at org.apache.wicket.markup.html.form.Form.process(
      	at org.apache.wicket.markup.html.form.Form.onFormSubmitted(
      	at org.apache.wicket.markup.html.form.Form.onFormSubmitted(
      	... 34 more
      My entity manager is configured as follows (I use the HibernateJpaVendorAdapter to indicate that Spring should adjust itself to the Hibernate JPA implementation):

          <property name=" value="MY_PU" />
          <property name="dataSource" ref="dataSource" />
          <property name="jpaVendorAdapter">
              <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                  <property name="databasePlatform" value="${database.platform}" />
                  <property name="showSql" value="${database.showSql}" />
                  <property name="generateDdl" value="${database.generateDdl}" />
          <property name="jpaProperties">
                  <prop key="hibernate.ejb.naming_strategy">
                  <prop key="">
      	    <prop key="hibernate.transaction.manager_lookup_class"> 
      <bean class="org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor" />
      Note that afaik, for AspectJ there is no need to register the "PersistenceExceptionTranslationPostProcessor" as it does not apply to @Configurable classes.

      In any case, I tried both with and without it (in the context above it is shown), with the same results.


      • #4
        Ah... The case you describe is (indeed) a bit different. I suspected you used the normal approach (didn't assume the AspectJ approach) and then the HibernateJpaDialect is addressed before the call to the method you mentioned. The JpaExceptionTranslatorAspect however doesn't do that, there is already an issue in JIRA for that. If you check the related issues you see a possible workaround (documented for Spring ROO).

        The workaround is basically a custom Aspect which delegates to a PersistenceExceptionTranslator.


        • #5
          Feedback appreciated!

          I guess I probably should have clarified the use of AspectJ it in the first place...

          It's nice to see that this is considered a bug. In fact, I had already created a custom aspect yesterday, in order to check for my specific case (intending to extend it as other cases show up), but now I see that I just need to call the hibernate translator in my aspect.

          Thanks for the feedback Martin!