当前位置: 首页 > 知识库问答 >
问题:

使用Spring数据JPA EntityGraph和LAZY加载模式作为NamedAttributeNode字段

端木皓君
2023-03-14

我面临两个问题:N 1查询和内存不足(OOM)。

我通过分页和延迟加载解决了OOM问题:

@OneToMany(fetch = FetchType.LAZY)
@JoinColumn(name = "department_id")
private Set<Employee> employees;

但当我使用延迟加载时,出现了N 1查询。因此,我尝试使用EntityGraph作为https://www.baeldung.com/spring-data-jpa-named-entity-graphs.但在我的研究和本地测试中,EntityGraph总是对NamedAttributeNode字段-关联字段(我想延迟加载)进行急切加载,而不是首先加载所有数据:

@Entity
@Table(name = "department")
@NamedEntityGraph(name = "Department",
        attributeNodes = {
                @NamedAttributeNode("employees")
        }
)
public class Department implements Serializable {
    @OneToMany(fetch = FetchType.LAZY)
    @JoinColumn(name = "department_id")
    private Set<Employee> employees;
}

那么有什么方法可以同时获得它们吗?使用EntityGraph避免N 1和延迟加载以避免OOM?

更新:EntityGraph能否有效地与Pageable配合使用?我的意思是不要在连接查询中加载所有数据

共有2个答案

王泓
2023-03-14

一个简单的解决方案是:

  • 首先获取带有分页但没有@EntityGraph的记录。
  • 使用@EntityGraph从以前返回的id中获取您的记录。

=

例子:

在主存储库中,添加带有要获取的关联的EntityGraph注释:

java prettyprint-override">public interface CarRepository extends JpaRepository<Car, String>, JpaSpecificationExecutor<Car>, CarCustomRepository {

    @Override
    @EntityGraph(attributePaths = { "foo", "bar", "bar.baz" })
    List<Car> findAllById(Iterable<String> ids);

}

使用findAllWithEntityGraph方法创建自定义存储库:

public interface CarCustomRepository {

    Page<Car> findAllWithEntityGraph(Specification<Car> specification, Pageable pageable);

}

自定义存储库的实现。它首先获取没有实体图的实体,然后使用实体图重新获取它们以加载关联。不要忘记对实体重新排序以保持顺序:

public class CarCustomRepositoryImpl implements CarCustomRepository {

    @Autowired
    @Lazy
    private CarRepository carRepository;

    @Override
    public Page<Car> findAllWithEntityGraph(Specification<Car> specification, Pageable pageable) {
        Page<Car> page = carRepository.findAll(specification, pageable);
        List<String> ids = page.getContent().stream().map(Car::getId).collect(Collectors.toList());
        List<Car> cars = carRepository.findAllById(ids).stream()
                .sorted(Comparator.comparing(car -> ids.indexOf(car.getId())))
                .collect(Collectors.toList());
        return new PageImpl<>(cars, pageable, page.getTotalElements());
    }

}

然后,您只需调用CarRepository#findAllSusEntityGraph方法即可获取具有分页且没有N 1复杂性的记录。

问题是:为什么hibernate在默认情况下没有这种行为?

姚兴安
2023-03-14

使用EntityGraph,您的所有关联都将加载到带有Join子句的1个查询中。启用sql日志以查看hibernate在不同场景中加载实体的查询数

logging.level.org.hibernate.SQL=DEBUG

您将看到,在不使用实体图的情况下,使用OneToMany(fetch=FetchType.EAGER)可以在单独的选择查询中加载员工(N 1),但使用实体图只执行1选择。。。加入

也不要忘记在存储库中指定实体图名称,例如:

@EntityGraph(value = "Department")
List<Department> findAll();

更新:Spring数据分页在数据库端不起作用。它将获取所有数据,然后在内存中进行过滤。这就是它的工作原理。。有一些解决方法,请检查以下链接:

使用Hibernate时,如何避免使用集合获取指定的警告“firstResult/max结果;在内存中应用!”?

避免“HH000104:使用集合获取指定的firstResult/maxResults;在内存中应用!”使用Spring数据

VladMihalcea博客修复Hibernate HHH000104的最佳方法

对我来说,解决方案可能是创建自定义存储库并使用EntityManager手动构建查询。

 类似资料:
  • 我两个月前刚开始学习Spring,以前从未做过Ajax或JavaScript。所以我对这个很陌生。我想做的是从控制器中的GET方法加载数据,将其填充到模式中。我正在使用ajax来实现这一点。基本上我做了这家伙做的事https://qtzar.com/2017/03/24/ajax-and-thymeleaf-for-modal-dialogs/正在做。但它不起作用。 希望有人能帮我。 这是我的控制

  • 我有个小问题。假设我有这个实体: 因为在某些时候我不想加载名字和姓氏,所以我使用了投影(https://docs.spring.io/spring-data/jpa/docs/1.10.2.release/reference/html/#Projections):我定义了这个接口: 现在来看看这个存储库: 所以基本上Spring加载所有字段。如果在PersonSlim接口中排除方法,Spring将

  • 描述 (Description) 延迟加载延迟一个给定的网页上您的图像加载过程。 延迟加载滚动提高性能,加快页面加载,节省流量。 懒惰负载元件和图像必须是内部滚动正常工作。 下表显示了使用延迟加载的 - S.No 懒惰负载和使用说明 1 Usage 延迟加载可以应用到图像,背景图像和与淡入效果。 2 Init Lazy Load Manually 初始化页面后,如果您添加延迟加载图像手动然后,延迟

  • 另外,我将.kt反编译回Java。在companion类中,我得到以下代码。 } 1)如何通过在kotlin同伴对象中使用lateinit或lazy来实现线程安全、单例?正如我所看到的,反编译的java代码在initialize函数中有一个synchronized调用,但在synchronize正文中没有。

  • 有什么方法可以声明惰性加载队列吗?

  • 对象文件: 零件文件: 用于加载数据的密码命令行: neo4j2.1.7和neo4j2.2.0-M04都存在此问题。