Service-Dao 模式、DTO 和关系数据库

Posted

技术标签:

【中文标题】Service-Dao 模式、DTO 和关系数据库【英文标题】:Service-Dao pattern, DTO and relational database 【发布时间】:2015-12-19 02:38:51 【问题描述】:

首先,很抱歉,如果这个主题已经被处理,但我没有找到我真正想要的东西。 我正在开发 ERP,我们正在尝试对代码进行一些重构。主要问题是我们目前不使用任何 DAO 模式,如果我们需要以不同的方式访问“数据库”,这可能会成为未来的问题。

简而言之,我们的架构将针对这种模式:

Bean 或 Web 服务称为我们所说的“事务层”(封装服务,以便可以通过 WS 公开某些内容并执行其他操作)。该层调用服务,这将调用其他服务或 DAO。

1) 实体

public class MyObject
    private String arg1;
    private List<SomeOtherObject> arg2List;


2) DAO

public interface MyObjectDAO 
    void save();
    List<MyObject> findAllObjects();
   // Some other queries
   // ...

3) 我的对象服务

@Service
public class MyObjectService
    @Autowired
    MyObjectDAO dao;

    @Autowired
    MyOtherObjectDAO otherDao;

    public void createObject(String arg1Dto, List<MyOtherObjectDto> arg2Dto)
       // How to deal with arg 2 ? 


        MyObject obj = new MyObject();
        obj.setArg1(arg1);
        obj.setArg2(myEntityRepresentingArg2);
        dao.save(obj1);
    

3) 交易层

public class
    // Many many things...

    //Method called from the Beans
    @Transactional(rollbackFor=Exception.class)
    public void serviceCall(SomeDto arguments)
        myObjectServices.createObject(arguments.getArg1(), arguments.getArg2());
    

我的问题是关于最佳实践的:

首先,我们使用 Hibernate 和 JPARepository 来管理实体。所以我猜对存储库的调用应该在 DAOImpls 中完成?我们对数据库进行的查询(即带有连接、选择等的 JPAQuery)和投影呢?这样 DAO 就会返回 DTO...

我们也不确定在哪里使用 DTO。在“事务层”中使用 DTO 与 DAO 中的实体之间应该有什么界限?是否应该将 DTO 传递给服务类,然后将实体完全传递给 DAO 层?或者我们应该只将参数传递给 DAO,然后它自己创建实体(问题是它会导致一些巨大的方法签名)。

非常感谢,如有需要,请随时提出问题!

【问题讨论】:

【参考方案1】: 在哪里使用 DTO?

通常,Service 方法获取 DTO 作为参数,并在其实现内部将此 DTO 转换/映射为实体,然后将其传递给存储库。

存储库(或 DAO)应该只知道实体,而不是 DTO,而其他层应该只知道 DTO,而不是实体。

总而言之,Service 类应该只接受和返回 DTO。 这样做是为了将模型及其细节隐藏在持久层之外。示例:

public class ProjectService 
    // The Repository should be an interface and Spring injects your Impl
    @Autowired
    private ProjectRepository projectRepository;

    public void createProject(ProjectDto dto) 
        // We map the Dto into an Entity
        Project project = new Project();
        project.setName(dto.getName);
        project.setDepartment(dto.getDepartment);        

        projectRepository.save(project);
    

    public ProjectDto findProject(Long id) 
        // Get Project entity
        Project project = projectRepository.findOne(id);
        // Map entity to dto
        ProjectDto dto = new ProjectDto();
        dto.setName(project.getName());
        dto.setDepartment(project.getDepartment());

        return dto;
    

如您所见,将有很多样板用于将实体映射到 dto,反之亦然。您可以将其封装在只进行转换的方法中,或者更好的是您可以使用映射库,例如 Orika 或 Dozer。

Here 是如何使用 Orika 并在需要时将其与 Spring 集成的示例。

关于 DAO 和 JPA 存储库

如果您使用的是 Spring Data JPA repositories,则不需要任何 DAO,您只需要存储库的接口,也许还需要该接口的实现。如果您愿意,您可以拥有 DAO 并在 Repository 实现中使用它们,但没有必要这样做。他们在参考documentation 上有很好的例子。

要执行 SQL 查询,您可以使用 @QuerynativeQuery=true 时可以采用 JPQL 或原生 SQL 查询。您可以找到更多信息here。

  @Query(value = "SELECT * FROM USERS WHERE EMAIL_ADDRESS = ?0", nativeQuery = true)
  User findByEmailAddress(String emailAddress);

希望对您有所帮助。

【讨论】:

【参考方案2】:

当数据跨越“服务/事务层”时,您绝对应该使用 DTO 模式。我写了一个article,讨论了不这样做时出现的一些常见问题,以及如何使用 Blaze-Persistence 实体视图有效地实现 DTO 方法。

也许您想尝试一下,而不是 Orika 或 Dozer,因为它也会提高您的查询性能。

如果您有 Spring Data JPA 存储库,则不再需要单独的 DAO。

【讨论】:

以上是关于Service-Dao 模式、DTO 和关系数据库的主要内容,如果未能解决你的问题,请参考以下文章

NestJS + Typeorm + Graphql:嵌套关系中 DTO 的正确设计模式

NestJS 与猫鼬模式、接口和 dto 方法问题

使用 Spring Boot 代替 DTO、Dao、Service 的设计模式? [关闭]

在spring中该如何使用DTO,以及DTO和Entity的关系

DTO和ENTITY的关系

具有业务对象、DTO 和实体/域对象的数据转换模式