避免N + 1与Hibernate实体上的DTO映射

时间:2014-06-12 19:24:58

标签: spring hibernate jpa transactions dto

在我们的Restful应用程序中,我们决定使用DTO来屏蔽Hibernate域模型,原因有几个。 我们使用DTOMappers中的Service Layer手动将Hibernate实体映射到DTO,反之亦然。

服务层中的示例:

@Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
    Person person = personRepository.findById(id);
    return PersonMapperDTOFactory.getInstance().toDTO(person);
}

主要概念可以这样解释:

JSON (Jackson parser) <-> Controller <-> Service Layer (uses Mapping Layer) <-> Repository

我们同意通过使用HQL执行Criteria(或left join)来检索关联。 这主要是一种检索关系并避免使用N+1 select issue的高效方法。

但是,当开发人员错误地忘记进行左连接时,仍然可以拥有N+1 select issue。仍将提取关系,因为PersonDTOMapper会迭代Invoices的{​​{1}}以转换为Person。因此,仍然会获取数据,因为执行InvoiceDTOs Hibernate会话处于活动状态(由DTOMapper管理)

是否有某种方法可以使Spring&#39;不活跃&#39;在我们的Hibernate Session?我们将面临一个DTOMappers,它应该触发开发人员,他没有像我们那样获取某些数据。

我已阅读暂停交易的LazyInitializationException。但是,我不知道它是出于这种目的。

实现这一目标的清洁解决方案是什么?替代品也非常受欢迎!

3 个答案:

答案 0 :(得分:0)

通常我在控制器层使用映射器。从我的角度来看,服务层管理应用程序业务逻辑,如果您想以不同的方式将数据呈现给外部世界,则dtos非常有用。通过这种方式,你可能会得到你正在寻找的懒惰的inizitalization excpetion。

我还有一个理由更喜欢这个解决方案:只需要在服务类的公共方法中调用公共方法的图像:在这种情况下,您可能需要多次调用映射器。

答案 1 :(得分:0)

如果您正在使用Hibernate,那么有一些特定的方法可以确定关联对象是否已延迟加载。

例如,假设您有一个实体类Foo,它包含与实体类@ManyToOne的{​​{1}}'外来'关联,由Bar中的字段表示叫Foo

在您的DTO映射代码中,您可以使用以下代码检查关联的bar是否已延迟加载:

bar

答案 2 :(得分:0)

实现所需目标的最简单解决方案是在查询之后和调用DTO映射器之前清除实体管理器。这样,该对象将被分离,并且对未初始化的关联的访问将触发LazyInitializationException

我也感到很痛苦,这促使我开发了Blaze-Persistence Entity Views,它允许您将DTO定义为接口,并使用属性名称作为默认映射来映射到实体模型,该映射允许非常简单的映射。 / p>

这里有个例子

@Entity
class Person {
  @Id Long id;
  String name;
  String lastName;
  String address;
  String city;
  String zipCode;
}

@EntityView(Person.class)
interface PersonDTO {
  @IdMapping Long getId();
  String getName();
}

查询就像

@Transactional(readOnly=true)
public PersonDTO findPersonWithInvoicesById(Long id) {
    return personRepository.findById(id);
}

interface PersonRepository extends EntityViewRepository<PersonDTO, Long> {
  PersonDTO findById(Long id);
}

由于您似乎正在使用Spring数据,因此您会喜欢spring data integration