使用spring data jpa的最高效的数据传输对象

时间:2018-10-31 22:38:09

标签: hibernate jpa spring-data-jpa

我有一个实体,在将其返回给用户之前需要稍作修改。据我了解,最好的方法是使用数据传输对象。

这很好,但是后来我添加了一个子实体。我再次为子实体创建了一个DTO。这是存储库的代码:

@Repository
public interface DrawingReleaseRepository extends CrudRepository<DrawingRelease, Integer> {
    @Query("SELECT new com.engineering.dto.drawings.DrawingReleaseDTO("
            + "dwgrel.drawingReleaseID,"
            + "relType"
            + ") FROM DrawingRelease dwgrel "
            + "JOIN dwgrel.releaseType relType "
            + "WHERE dwgrel.drawingReleaseID = ?1")
    DrawingReleaseDTO getDrawingRelease(int drawingReleaseID);
}

DrawingReleaseDTO的构造函数是这样的:

public DrawingReleaseDTO(int drawingReleaseID, DrawingReleaseType releaseType) {
    this.drawingReleaseID = drawingReleaseID;
    this.releaseType = new DrawingReleaseTypeDTO(releaseType);
}

在服务层中,我现在可以操纵数据并将其返回给客户端,但是更改不会持久保存到数据库中(这是我想要的):

@Override
public DrawingReleaseDTO findByID(int id) {
    DrawingReleaseDTO dto = repository.getDrawingRelease(id);
    dto.getReleaseType().setCustom1(true);
    return dto;
}

该方案的问题是效率不是很高。我的意思是Hibernate执行两个查询-一个查询DrawingRelease和一个查询DrawingReleaseType

我认为解决此问题的方法将是指定JOIN FETCH dwgrel.releaseType relType,因为这将强制休眠一次获取所有数据,但这会产生错误:

  

查询指定联接获取,但已获取的所有者   选择列表中不存在关联

是否有使用DTO的方法,包括子实体的数据,并全部使用有效的查询?

2 个答案:

答案 0 :(得分:1)

问题是您将DTO和实体混合在一起。

DTO不应引用实体,第二个查询来自初始化子实体。

此外,将实体传递给DTO也是一种反模式:

this.releaseType = new DrawingReleaseTypeDTO(releaseType);

您需要从列投影构造DTO。有关更多详细信息,请查看this article

答案 1 :(得分:0)

这是Blaze-Persistence Entity Views的完美用例,它将通过生成仅获取相关属性的JPQL / HQL查询来完全满足您的要求,并提高性能。 尽管我不了解您的模型,但据我了解,在您的模型中使用实体视图可能像这样

interface DrawingReleaseRepository extends JpaRepository<DrawingReleaseDTO, Long> {
}

DrawingReleaseDTO dto = drawingReleaseRepository.findById(id);

setup正确时,您可以将该实体视图与spring-data存储库一起使用

SELECT drawingRelease.id, drawingReleaseType.custom1 
FROM DrawingRelease drawingRelease 
LEFT JOIN drawingRelease.drawingReleaseType drawingReleaseType 
WHERE drawingRelease.id = :id

它将生成这样的JPQL / HQL查询

type COMPANY {
    "AIRLINE SEGMENT": String             
    AIRLINE_TYPE: String
}