Hibernate JAX-RS中的LazyInitializationException和DTO

Hibernate JAX-RS中的LazyInitializationException和DTO,hibernate,rest,jax-rs,entity,dto,Hibernate,Rest,Jax Rs,Entity,Dto,在我的RESTful后端应用程序中,有两个实体(A和B)具有以下关系:一个实体A可以与多个实体B相关联,因此在A中,我们有一个用@OneToMany注释的B实体集合 实施MVC模式,我有: 资源层(用户/前端和我的应用程序之间的接口) 服务层(调用DAO在数据库上执行查询的EJB) 在资源层中检索a实体时,我遇到了一个LazyInitializationException。通过四处搜索,我了解到问题源于这样一个事实:当服务层返回实体A时,它会将其从持久性层中删除,并且由于默认情况下集合是延迟

在我的RESTful后端应用程序中,有两个实体(A和B)具有以下关系:一个实体A可以与多个实体B相关联,因此在A中,我们有一个用@OneToMany注释的B实体集合

实施MVC模式,我有:

  • 资源层(用户/前端和我的应用程序之间的接口)
  • 服务层(调用DAO在数据库上执行查询的EJB)
在资源层中检索a实体时,我遇到了一个LazyInitializationException。通过四处搜索,我了解到问题源于这样一个事实:当服务层返回实体A时,它会将其从持久性层中删除,并且由于默认情况下集合是延迟加载的,如果我尝试在资源层中获取它们,我会收到异常

网络上引用最多的解决方案之一是使用DTO对象,并在服务层将实体转换为DTO

我的第一个问题与最后一段有关: 这种方法只是使用实体的“get”方法,从而强制ORM加载集合的一个技巧吗? 我这么说是因为当我们将实体映射到DTO时,我们会执行以下操作:

objectADTO.setB(objectAEntity.getB());
使用此操作,我们正在从DB检索B的集合。我们可以通过在服务层编写一行类似这样的代码来实现相同的结果:
objectAEntity.getB()
,然后直接将objectAEntity返回到服务层,因为我们使用了.getB()检索实际值

现在,您可以说:DTO对象允许您具有更多的通用性,但是我的第二个问题是: 我是否必须根据我需要的服务层中的方法在实体a和DTO a之间定义不同的映射? 换句话说,如果我有两个方法返回不同的数据类型信息,我应该处理两种DTO映射吗?(例如,考虑A包含两个不同的图像集合,方法1需要第一个集合,而第二个方法需要第二个集合。我不能使用将所有集合提供给这两个方法的同一DTO映射,因为它将返回太重的对象)

我希望我在描述我的问题时说得很清楚


提前感谢您的回答

您可以以一种只映射Java属性并在转换期间触发延迟加载的方式实现DTO,但这可能会导致臭名昭著的N+1延迟加载问题。如果只加载DTO真正需要的数据,性能会更好

DTO并不总是实体的子集,有时还包含转换的信息或间接相关实体的信息。因此,每个用例都有一个专用的类型,这在我看来绝对是可取的

我是否必须在实体a和DTO a之间定义不同的映射, 基于我需要的服务层中的方法?换句话说,, 如果我有两个方法返回不同的A的信息,我应该 处理两种DTO映射

是的,每个用例都应该有自己的DTO。如果需要,您可以通过继承或组合来实现可重用性

这是一个完美的用例

我创建了这个库,以便在JPA模型和自定义接口或抽象类定义的模型之间进行简单的映射,类似于类固醇上的Spring数据投影。其思想是以您喜欢的方式定义目标结构(域模型),并通过JPQL表达式将属性(getter)映射到实体模型

在Blaze持久性实体视图中,您的用例的DTO模型可能如下所示:

@EntityView(A.class)
public interface ADto {
    @IdMapping
    Long getId();
    String getName();
    @Mapping("bCollection")
    Set<BDto> getBs();

    @EntityView(B.class)
    interface BDto {
        @IdMapping
        Long getId();
        String getName();
    }
}
@EntityView(A.class)
公共接口ADto{
@IdMapping
长getId();
字符串getName();
@映射(“b收集”)
设置getBs();
@实体视图(B.class)
接口BDto{
@IdMapping
长getId();
字符串getName();
}
}
查询是将实体视图应用于查询的问题,最简单的就是按id进行查询

ADto a=entityViewManager.find(entityManager,ADto.class,id)


Spring数据集成允许您像使用Spring数据投影一样使用它:

谢谢Christian,解释得非常清楚。对我来说,为同一个实体创建许多DTO听起来很奇怪,但如果它们通常包含图像或LOB,则特别有意义。我会等几天来检查你的答案,看看是否还有其他人想回应。你为什么认为这很奇怪?您有不同的用例,那么为什么不使用不同的DTO呢?一旦要在表中显示实体,然后要在选择输入中显示实体,然后要将实体作为一个整体显示。三个不同的用例,每个用例都应该有一个自定义DTO,因为您显示的数据不同。重复使用相同的DTO是一种浪费: