Can Spring DAO be merged into Service layer?

2019-09-12 00:53发布

问题:

I'm developing a web application with spring framework and mybatis.

In most cases(at least for me), DAO's methods are very short something like this:

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return (User) getSqlSession().selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

So basically, I need to write a method(e.g. getUser(String userId)) in DAO for each query which is being forwarded to service objects where it is being used. It seems unnecessarally redundunt to me.

My co-worker is trying to make it simple. He wrote CommonDao like this:

@Repository
public class CommonDao {
    @Autowired
    private SqlSessionTemplate sqlSession;

    public Object insert(String queryId, Object params) {
        return sqlSession.insert(queryId, params);
    }

    public Object update(String queryId, Object params) {
        return sqlSession.update(queryId, params);
    }

    public Object delete(String queryId, Object params) {
        return sqlSession.delete(queryId, params);
    }

    public Object selectOne(String queryId) {
        return sqlSession.selectOne(queryId);
    }

    public Object selectOne(String queryId, Object params) {
        return sqlSession.selectOne(queryId, params);
    }
}

So we can use these methods in services like:

@Service
public class CrudService {
    ...
    @Autowired
    private CommonDao commonDao;
    ...

    public UserDto selectUser(Integer userId) {
        ...
        UserDto userDto = (UserDto) commonDao.selectOne("org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
        ...
    }
}

I'm kinda like this approch since it makes codes simpler. But I'm not sure it is a good prectice to follow.

回答1:

To avoid a boilerplate code and at the same time have type safety and leave your service layer free from DAO implementation details use spring-mybatis MapperScannerConfigurer.

In this case you can replace your DAOs with type-safe mappers.

The equivalent of your DAO

public class UserDaoImpl extends SqlSessionDaoSupport implements UserDao {
  public User getUser(String userId) {
    return (User)getSqlSession().selectOne(
        "org.mybatis.spring.sample.mapper.UserMapper.getUser", userId);
  }
}

will be this mapper class

package org.mybatis.spring.sample.mapper;

interface UserMapper {
  User getUser(String userId);
}

If you rename it to UserDao you will not need to change your services at all. Service only depends on the declared mapper interface.

Note that you need to define this interface in order to have type safety and also define the dependency of you service.

Of course you need configure spring-mybatis so that it generates mapper implementation based on the mapper interfaces defined in your code. This is rather straightforward and there are many options how to do that.



回答2:

Hmm, what you are struggling with, is normal in MyBatis.

Your co-worker pointed you in some direction... But what is the real value of CommonDao in this shape? For me it's not very useful. You still need almost the same amount of code - and still you have to do a lot of casting.

As @Rom Konoval said, there is MapperScannerConfigurer which can generate mapper implementations - this way you don't write redundant implementations and have the benefit of a type safety - type casting still happens but is hidden from you. You can try it.
Here is a sample usage on the GitHub.

Alternatively, you can create DAO implementations by yourself (as you already did) or just use the SqlSessionTemplate directly in your Services. Up to you. Just keep your code base as small as possible and follow a common sense.