6

I'm currently working with MyBatis-Spring integration framework and that's what I read from docs:

Rather than code data access objects (DAOs) manually using SqlSessionDaoSupport or SqlSessionTemplate, Mybatis-Spring provides a proxy factory: MapperFactoryBean. This class lets you inject data mapper interfaces directly into your service beans. When using mappers you simply call them as you have always called your DAOs, but you won't need to code any DAO implementation because MyBatis-Spring will create a proxy for you.

That's a very nice feature... but what about exception handling? Where should I translate SQL errors? In my service layer? But wouldn't it violate service-DAO patterns?

Example:

public final class AccountServiceImpl implements AccountService {
(...)
    private AccountMapper accountMapper;
(...)
    @Override
    public void addAccount(Account account) throws AccountServiceException {

       //Validating, processing, setting timestamps etc.
       (...)

       //Persistence:
       int rowsAffected;
       try {
            rowsAffected = accountMapper.insertAccount(account);
       } catch (Exception e) {
            String msg = e.getMessage();
            if (msg.contains("accounts_pkey"))
                throw new AccountServiceException("Username already exists!");
            if (msg.contains("accounts_email_key"))
                throw new AccountServiceException("E-mail already exists!");
            throw new AccountServiceException(APP_ERROR);
       }

       LOG.debug("Rows affected: '{}'", rowsAffected);

       if (rowsAffected != 1)
            throw new AccountServiceException(APP_ERROR);
    }

Is it OK to translate exceptions in service layer?

How should it be done?

Thanks in advance for you advice.

Maciej Ziarko
  • 11,494
  • 13
  • 48
  • 69

2 Answers2

7

Having recently used mybatis-spring for a project I came across the same stumbling block. I also didn't want to litter my service class with DAO exception handling, particularly since some methods in my service layer required read-only access to a lot of different tables.

The solution I arrived at was to catch the exceptions in the service layer but create your own exception type that takes the caught exception as a parameter. This can then filter out what kind of error message should be contained when the exception is actually constructed and remove the need for string matching (in the service layer at least).

You are close to that there, except the AccountServiceException would have a constructor that took the Exception e as a parameter. I also chose to try and do all my data access as early as possible and wrap it all in a single try/catch. Since the MapperFactoryBean always translates thrown exceptions in to Spring DataAccessExceptions you don't have to worry about catching other kinds of exceptions when doing data access.

I hesitate to consider this an answer as such - more of a sharing of experience given I came across that and hesitated as well.

BeRecursive
  • 6,286
  • 1
  • 24
  • 41
2

Translating low level DataAccessExceptions thrown by MyBatis to application-defined ones in service layer is a standard practice.

It's usually connected to transaction handling as you can't handle the transaction spanning multiple DAOs in DA layer.

So yes it's OK and even recommended.

Normally I log the exceptions thrown by DAO in error log and rethrow something defined by application.

soulcheck
  • 36,297
  • 6
  • 91
  • 90