我们目前正试图找出解决常见问题的最佳解决方案。以下是上下文:
上下文:
这就是我们的项目如何组成
- 我们有
model
类,它们确保传递的数据是有效的 - 我们有
domain
类,它们是带有JPA注释的简单POJO
用例
当请求到达REST API时,将接收格式化为model
对象的对象(作为JSON)
然后将该对象转换为要持久化的domain
对象。最后,持久化对象被转换回model
对象以发送回视图。
问题
当我们将model
对象转换为domain
对象时,我们必须处理子对象
但在某些情况下,model
对象没有加载子对象,然后我们会遇到LazyLoading异常。
具体示例:
model
类
public class Classroom {
private final String name;
private final RoomCapacity roomCapacity;
private final Set<RoomEquipment> equipments = new HashSet<>();
private Long id;
@JsonCreator
public Classroom(@JsonProperty("name") final String name, @JsonProperty("roomCapacity") final RoomCapacity roomCapacity) {
if (StringUtils.isBlank(name)) {
throw new IllegalArgumentException("Cannot build a " + getClass().getName() + " without a name.");
}
if (roomCapacity == null) {
throw new IllegalArgumentException("Cannot build a " + getClass().getName() + " without a " + RoomCapacity.class.getName());
}
this.name = name;
this.roomCapacity = roomCapacity;
}
}
domain
级
@Entity
@Table(name = "classroom")
public class ClassroomDomain implements ModelTransformable<Classroom, Long> {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "CLASSROOM_ID")
private Long id;
@Column(unique = true)
private String name;
@OneToMany(mappedBy = "primaryKey.classroom", cascade = CascadeType.REMOVE)
private Set<RoomEquipmentDomain> equipments = new HashSet<>();
private int capacity;
public ClassroomDomain(Classroom classroom) {
if (classroom == null) {
throw new IllegalArgumentException("Cannot instantiate a " + getClass().getName() + " with a null " + Classroom.class.getName());
}
id = classroom.getId();
name = classroom.getName();
capacity = classroom.getRoomCapacity().getMaxCapacity();
classroom.getEquipments().forEach(e -> equipments.add(new RoomEquipmentDomain(e, this)));
}
@Override
public Classroom toModel() {
Classroom classroom = new Classroom(name, new RoomCapacity(capacity));
classroom.setId(id);
equipments.forEach(e -> classroom.addEquipment(e.toModel()));
return classroom;
}
}
正如您所看到的,domain
类有一个接受model
对象的构造函数。并且可以将CCD_ 13转换为模型。
因此,当我需要将domain
转换为model
时,它会失败,因为在某些情况下,我不加载equipments
列表,然后我会遇到LazyLoading异常。
当我在DAO中调用toModel()
时,它崩溃了。
public Classroom findOneById(Long id) {
if (id == null) {
throw new IllegalArgumentException("Cannot find a " + Classroom.class.getName() + " with a null id.");
}
ClassroomDomain domain = classroomRepository.findOne(id);
if (domain == null) {
throw new ClassroomNotFoundException("No " + Classroom.class.getName() + " found for id :" + id);
}
return domain.toModel();
}
限制
- 我们希望保留
model
类,因为这个应用程序需要是多个程序的公共API,所以我们需要一个实体模型 - 我们不想把所有关系都作为
EAGER
加载
问题
我们如何在不遇到异常的情况下将数据从domain
转换为model
,在这种情况下有哪些最佳实践。
我认为您可以使用Open Session in View (or Transaction in View)
设计模式,在用户请求结束之前,您将打开数据库连接。
当应用程序访问懒惰集合时,Hibernate/JPA将毫无问题地执行数据库查询,不会引发异常。
请参考:来源1,来源2
希望这能有所帮助。
有一个名为Blaze Persistence实体视图的库。您仍然需要两个方法,但如果需要,它们可以重用相同的查询逻辑,因为实体视图应用于现有查询。请注意,这也将提高性能,因为它只会获取实际映射的数据。
我甚至为你的精确用例提供了一个例子,一个外部模型。通过从外部模型扩展并通过构造函数映射传递数据,可以保持外部模型的独立性,同时获得良好的性能,避免使用EAGER
。
我最终决定使用:
- 只返回对象的方法
- 返回具有嵌套关联的对象的方法。(带有JPQL请求)
不是最好的解决方案,但我找不到其他解决方案。