JAX-RS 中的 LazyInitializationException 和 DTO
LazyInitializationException and DTO in JAX-RS
在我的 RESTful 后端应用程序中有两个实体(A 和 B)具有以下关系:一个实体 A 可以与许多实体 B 相关联,因此在 A 中我们有一组 B 实体注释@OneToMany.
实现 MVC 模式,我有:
- 资源层(user/frontend 和我的应用程序之间的接口)
- 服务层(调用 daos 对数据库执行查询的 ejbs)
我在资源层中检索 A 实体时出现 LazyInitializationException。四处搜索,我了解到问题源于这样一个事实,即当服务层 return 是实体 A 时,它将它与持久层分离,并且由于默认情况下集合是延迟加载的,如果我试图让它们进入资源层,我收到异常。
网络上引用最多的解决方案之一是使用 DTO 对象并在服务层将实体转换为 DTO。
我的第一个问题与最后一段有关:
这种方法只是使用实体的“获取”方法并因此强制 ORM 加载集合的技巧吗?
我这么说是因为当我们将 Entity 映射到 DTO 时,我们做了以下操作:
objectADTO.setB(objectAEntity.getB());
使用这个操作,我们是从DB中取出B的集合,我们可以在服务层简单的写一行这样的代码:objectAEntity.getB()
,然后直接return 到服务层 objectAEntity,它不会引发异常,因为我们使用 .getB() 来检索实际值。
现在,您可以说:DTO 对象可以让您拥有更多功能,但是 我的第二个问题 是:
我是否必须根据我需要的服务层中的方法在实体 A 和 DTO A 之间定义不同的映射?
换句话说,如果我有两种方法 return 的 A 的不同信息,我应该处理两种 DTO 映射吗? (例如,考虑 A 包含两个不同的图像集合,方法 1 想要第一个集合,而第二个方法想要第二个。我不能使用为这两种方法提供所有集合的相同 DTO 映射,因为它会 return 一个太重的物体)
我希望我在描述我的问题时是清楚的。
提前感谢您的回答
您可以通过仅映射 Java 属性并在转换期间触发延迟加载的方式实现 DTO,但这可能会导致您遇到臭名昭著的 N + 1 延迟加载问题。如果只加载 DTO 真正需要的数据,性能会好很多。
DTO 并不总是实体的子集,有时还包含转换后的信息或关于间接相关实体的信息。因此,每个用例都有一个专用类型绝对是 IMO 所希望的。
do I have to define a different mapping between entity A and DTO A,
based on the methods in the service layer that I need? In other words,
if I have two methods that returns different info of A, should I
handle two kinds of DTO mapping?
是的,每个用例都应该有自己的 DTO。如果需要,您可以通过继承或组合实现可重用性。
这是 Blaze-Persistence Entity Views 的完美用例。
我创建了库以允许在 JPA 模型和自定义接口或抽象 class 定义的模型之间轻松映射,类似于 Spring 类固醇数据投影。这个想法是您按照自己喜欢的方式定义目标结构(领域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
您的用例的 DTO 模型可能如下所示 Blaze-Persistence Entity-Views:
@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();
}
}
查询就是将实体视图应用于查询,最简单的就是通过 id 进行查询。
ADto a = entityViewManager.find(entityManager, ADto.class, id);
Spring 数据集成让您几乎可以像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features
在我的 RESTful 后端应用程序中有两个实体(A 和 B)具有以下关系:一个实体 A 可以与许多实体 B 相关联,因此在 A 中我们有一组 B 实体注释@OneToMany.
实现 MVC 模式,我有:
- 资源层(user/frontend 和我的应用程序之间的接口)
- 服务层(调用 daos 对数据库执行查询的 ejbs)
我在资源层中检索 A 实体时出现 LazyInitializationException。四处搜索,我了解到问题源于这样一个事实,即当服务层 return 是实体 A 时,它将它与持久层分离,并且由于默认情况下集合是延迟加载的,如果我试图让它们进入资源层,我收到异常。
网络上引用最多的解决方案之一是使用 DTO 对象并在服务层将实体转换为 DTO。
我的第一个问题与最后一段有关: 这种方法只是使用实体的“获取”方法并因此强制 ORM 加载集合的技巧吗? 我这么说是因为当我们将 Entity 映射到 DTO 时,我们做了以下操作:
objectADTO.setB(objectAEntity.getB());
使用这个操作,我们是从DB中取出B的集合,我们可以在服务层简单的写一行这样的代码:objectAEntity.getB()
,然后直接return 到服务层 objectAEntity,它不会引发异常,因为我们使用 .getB() 来检索实际值。
现在,您可以说:DTO 对象可以让您拥有更多功能,但是 我的第二个问题 是: 我是否必须根据我需要的服务层中的方法在实体 A 和 DTO A 之间定义不同的映射? 换句话说,如果我有两种方法 return 的 A 的不同信息,我应该处理两种 DTO 映射吗? (例如,考虑 A 包含两个不同的图像集合,方法 1 想要第一个集合,而第二个方法想要第二个。我不能使用为这两种方法提供所有集合的相同 DTO 映射,因为它会 return 一个太重的物体)
我希望我在描述我的问题时是清楚的。
提前感谢您的回答
您可以通过仅映射 Java 属性并在转换期间触发延迟加载的方式实现 DTO,但这可能会导致您遇到臭名昭著的 N + 1 延迟加载问题。如果只加载 DTO 真正需要的数据,性能会好很多。
DTO 并不总是实体的子集,有时还包含转换后的信息或关于间接相关实体的信息。因此,每个用例都有一个专用类型绝对是 IMO 所希望的。
do I have to define a different mapping between entity A and DTO A, based on the methods in the service layer that I need? In other words, if I have two methods that returns different info of A, should I handle two kinds of DTO mapping?
是的,每个用例都应该有自己的 DTO。如果需要,您可以通过继承或组合实现可重用性。
这是 Blaze-Persistence Entity Views 的完美用例。
我创建了库以允许在 JPA 模型和自定义接口或抽象 class 定义的模型之间轻松映射,类似于 Spring 类固醇数据投影。这个想法是您按照自己喜欢的方式定义目标结构(领域模型),并通过 JPQL 表达式将属性(getter)映射到实体模型。
您的用例的 DTO 模型可能如下所示 Blaze-Persistence Entity-Views:
@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();
}
}
查询就是将实体视图应用于查询,最简单的就是通过 id 进行查询。
ADto a = entityViewManager.find(entityManager, ADto.class, id);
Spring 数据集成让您几乎可以像 Spring 数据投影一样使用它:https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features