使用hibernate / spring一次加载所有延迟集合

时间:2017-08-08 19:11:15

标签: java spring hibernate spring-data-jpa

我有一个问题,加载我的懒惰集合会产生大量的SQL语句,我想知道是否没有更有效的方法来加载数据。

情况:

Parent 有一个名为children的 Child 的惰性集合。它实际上是一种多对多的关系。

我使用CrudRepository加载 Parent 的列表,我需要为每个 Parent 获取所有child_ids。所以每次我访问子集合时,我都会执行一个新的SQL语句。

如果我加载了200个父母,则会执行201个查询(1个表示父母列表,1个表示每个父母的孩子)。

知道如何只用一个查询加载数据吗?

编辑1

父/子在这里可能是一个糟糕的命名。事实上,我有一个多对多的关系。

以下是一些代码:

@Entity
public class Tour {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name="system-uuid",
        strategy = "uuid2")
    @Column(length = 60)
    private String id;

    @ManyToMany
    @JoinTable(
        name="parent_images",
        joinColumns = @JoinColumn(name="tour_id", referencedColumnName = "id"),
        inverseJoinColumns = @JoinColumn(name="image_id", referencedColumnName = "id"),
        foreignKey = @ForeignKey(name = "FK_TOUR_IMAGE_TOUR"),
        inverseForeignKey = @ForeignKey(name = "FK_TOUR_IMAGE_IMAGE")
    )
    private List<Image> images = new ArrayList<>();
}

@Entity
public class Image {
    @Id
    @GeneratedValue(generator = "system-uuid")
    @GenericGenerator(name="system-uuid",
        strategy = "uuid2")
    @Column(length = 40)
    private String id;

    //....
}

// Code to fetch:
@Autowired
TourRepository repo;
List<Tour> tours = repo.findBy(....);


List<String> imageIds = new ArrayList<>();

for(Tour tour : tours){
    imageIds.addAll(tour.getImages().stream().map(b -> b.getId()).collect(Collectors.toList()));
}

2 个答案:

答案 0 :(得分:1)

这是懒惰集合背后的整个想法:)

意思是,只有在调用该集合的getter时才会查询一个惰性集合,你所说的是你加载所有实体和某些东西(代码,框架,等等)调用getChildren(假设)该实体;这将产生这些查询。

现在,如果这种情况总是发生,那么首先,有一个懒惰的集合没有意义,将它们设置为EAGER - 编辑:如评论中所述, EAGER很少是解决方案,在这种情况下,特别是肯定看起来不是这样,但是加入是:)

无论哪种方式,对于你的情况都无济于事,你想要的是一次加载所有数据我假设,为此,当你进行查询时你必须使连接显式,例如使用JPQL:

SELECT p FROM Parent p LEFT JOIN FETCH p.children

答案 1 :(得分:1)

正如另一个答案所示,JOIN FETCH通常是解决类似问题的方法。 join-fetch内部发生的是生成的SQL将包含join-fetched实体的列。

但是,你不应盲目对待加入 - 获取是灵丹妙药。

一种常见情况是您想要检索具有2个一对多关系的实体。例如,您有User,每个User可能有多个AddressPhone

如果您天真地做from User user join fetch user.phones join fetch users.addresses,Hibernate会在您的查询中报告问题,或者生成包含笛卡尔地址和电话产品的低效查询。

在上面的例子中,一种解决方案是将其分解为多个查询: from User user join fetch user.phones where ....后跟from User user join fetch user.addresses where ....

请记住:较少的SQL数并不总是意味着更好的性能。在某些情况下,分解查询可能会提高性能。