JPA实体关系获取最佳实践



我们目前正试图找出解决常见问题的最佳解决方案。以下是上下文:

上下文:

这就是我们的项目如何组成

  • 我们有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请求)

不是最好的解决方案,但我找不到其他解决方案。

最新更新