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 查询,您可以使用 @Query
。 nativeQuery=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 的正确设计模式
使用 Spring Boot 代替 DTO、Dao、Service 的设计模式? [关闭]