Spring Boot + JPA + Exception Handling

11,005

Solution 1

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();

Solution 2

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?

Share:
11,005
Gnana
Author by

Gnana

Updated on July 17, 2022

Comments

  • Gnana
    Gnana almost 2 years

    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)
    
  • Gnana
    Gnana about 8 years
    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?
  • DominicEU
    DominicEU about 8 years
    NoResultException is a runtime exception, you'd have to catch it inside of the find(...) method.
  • Gnana
    Gnana about 8 years
    Thanks for the suggestion. Lot of refactoring needs to be done :)