Spring DAO能否合并到Service层?

Can Spring DAO be merged into Service layer?

我正在使用 spring 框架和 mybatis 开发 Web 应用程序。

在大多数情况下(至少对我而言),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);
  }
}

所以基本上,我需要在 DAO 中为每个被转发到使用它的服务对象的查询编写一个方法(例如 getUser(String userId))。这对我来说似乎是不必要的冗余。

我的同事正在努力让它变得简单。他这样写CommonDao

@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);
    }
}

所以我们可以在如下服务中使用这些方法:

@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);
        ...
    }
}

我有点喜欢这个方法,因为它使代码更简单。但我不确定这是一个值得遵循的好习惯。

嗯,你的纠结,在 MyBatis 中是正常的。

您的同事为您指出了某个方向...但是 CommonDao 在这个形状中的真正价值是什么?对我来说这不是很有用。您仍然需要几乎相同数量的代码 - 而且您仍然需要进行大量转换。

正如@Rom Konoval 所说,MapperScannerConfigurer 可以生成映射器实现 - 这样您就不会编写冗余实现并具有类型安全的好处 - 类型转换仍然发生但被隐藏了你。你可以试试。
这是 GitHub 上的 sample usage

或者,您可以自己创建 DAO 实现(就像您已经做的那样),或者直接在您的服务中使用 SqlSessionTemplate。由你决定。保持您的代码库尽可能小并遵循常识。

要避免样板代码并同时具有类型安全性并使您的服务层不受 DAO 实现细节的影响,请使用 spring-mybatis MapperScannerConfigurer

在这种情况下,您可以用类型安全的映射器替换您的 DAO。

相当于你的 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);
  }
}

将是这个映射器 class

package org.mybatis.spring.sample.mapper;

interface UserMapper {
  User getUser(String userId);
}

如果将其重命名为 UserDao,则根本不需要更改服务。服务仅依赖于声明的映射器接口。

请注意,您需要定义此接口以确保类型安全并定义服务的依赖性。

当然你需要配置 spring-mybatis 以便它根据你的代码中定义的映射器接口生成映射器实现。这相当简单,并且有 many options 如何做到这一点。