2

I'm working on a proof of concept to convert our project to a Spring Boot application. I have a repository class with 2 methods: save and find.

@Repository
public class UserDataRepo {
    private EntityManager em;

    public boolean save(UserDataModel model) {
        try {
            UserDataModel existingModel = find(model.getTable(), model.getFieldName();
            model.setId(existingModel.getId());
            this.em.merge(model);
            this.em.flush();
            return false;
        } catch (NoResultException e) {
            this.em.persist(model);
            this.em.flush();
            return true;
        }
    }

    public UserDataModel find(String table, String field) {
        Query query = this.em.createQuery(FIND_USERDATA_STATEMENT);
        query.setParameter("table", table);
        query.setParameter("fieldName", field);
        return (UserDataModel) query.getSingleResult(); // throws NoResultException
    } 
}

In my Spring Boot Application class, I have added @EnableJpaRepositories, @EnableTransactionManagement. My application starts up fine without any errors. But as you can see the save method depends on the find method to determine whether to merge or persist. If there is no record, find method throws NoResultException. What I'm observing is it never falls inside the save method's catch block. Spring Boot just throws an error saying NoResultException.

In the case of merge, it works like a charm. So it means the entity manager works fine.

I do not know what else needs to be configured. Any thoughts?

Adding error from the logs:

org.springframework.dao.EmptyResultDataAccessException: No entity found for query; nested exception is javax.persistence.NoResultException: No entity found for query
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:389)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:223)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
Perneel
  • 3,317
  • 7
  • 45
  • 66
Gnana
  • 614
  • 1
  • 7
  • 18
  • It looks like the exception you want to catch is `org.springframework.dao.EmptyResultDataAccessException` instead of `NoResultException`. The `NoResultException` is nested inside the outer exception so your `try { } catch` block won't see it – tddmonkey Feb 01 '16 at 20:59
  • @MrWiggles I changed it to catch EmptyResultDataAccessException, still it does not fall inside the catch block. Should Exceptions be mapped differently while using Spring Boot or the way persistence is configured using Spring Boot? – Gnana Feb 01 '16 at 21:24
  • You could change your catch block to `Throwable` temporarily to see what the actual Exception being raised is. – tddmonkey Feb 01 '16 at 21:33
  • @MrWiggles I tried and the actual exception is the same: javax.persistence.NoResultException: No entity found for query. To give a context, the existing web application is spring+jersey+hibernate and it is deployed as a war in tomcat. The logic works fine. The exception gets caught in the catch block inside save. I'm working on this POC to showcase whether the same can be bundled as a shaded jar and configure it as a Spring Boot application. Every micro-service seems to work except this logic where the exception does not get propagated to the parent block. – Gnana Feb 02 '16 at 21:55
  • @MrWiggles I just added "throws NoResultException" to the find method and it worked when I started the application using Spring Boot. Strange though. – Gnana Feb 02 '16 at 22:06

3 Answers3

5

I would not recommend this behaviour. Use of getSingleResult() like this is not recommended. Read more information on why never to use getSingleResult in JPA here. Instead try a query like this.

    Query query = this.em.createQuery(FIND_USERDATA_STATEMENT);
    query.setParameter("table", table);
    query.setParameter("fieldName", field);

    List results = query.getResultList();
    if (results.isEmpty()) {
        return null;
    }
    else if (results.size() == 1)  {
        return results.get(0);
    }
    throw new NonUniqueResultException();
DominicEU
  • 3,585
  • 2
  • 21
  • 32
  • I agree with you and the article that you have referred. Since this is a production code and there are other refactoring that needs to be done. But any reason why Spring Boot is not able to cascade/translate the exception up and fall inside the catch block? – Gnana Feb 01 '16 at 20:49
  • 1
    ```NoResultException``` is a runtime exception, you'd have to catch it inside of the ```find(...)``` method. – DominicEU Feb 01 '16 at 20:50
4

For simple persistence cases like this, I'd very strongly suggest looking at Spring Data JPA which would allow you to avoid having to do any of this complicated manual persistence management. If this does not work for you, i'd strongly suggest you not use Exceptions for Logic. That is, instead of relying on an exception from a failed Find query to determine if the object needs to be persisted or merged, I'd first query using a count to determine if the object exists, then, depending on that result continue with the merge or persist. In general using Exceptions in business logic is considered an anti pattern, please see: When is it OK to use exception handling for business logic?

Community
  • 1
  • 1
Ben M
  • 1,833
  • 1
  • 15
  • 24
0

I just added "throws NoResultException" to the find method and it worked. Not sure why this has to be specifically added to the signature while configuring the application in Spring Boot.

Gnana
  • 614
  • 1
  • 7
  • 18