gpt4 book ai didi

java - Spring JPA + Hibernate OneToOne 关系进行 n+1 查询

转载 作者:太空宇宙 更新时间:2023-11-04 11:51:58 27 4
gpt4 key购买 nike

我正在使用 Spring Boot、Groovy 和 JPA/Hibernate 来迁移旧应用程序。唯一的限制是数据库模型不得更改,我发现自己遇到了一种奇怪的情况,其中存在 OneOnOne 关系:

请查看以下模型设置:

@Entity
@Table(name='table1')
class Table1 {

@Id
Table1Id id

@Column(name='sequence_num')
Integer seqNum

@Column(name='item_source')
String itemSource

@Column(name='source_type')
String sourceType

@OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@JoinColumn(name='key_field_2', insertable=false, updatable=false)
@NotFound(action=NotFoundAction.IGNORE)
// @Fetch(FetchMode.JOIN)
Table2 table2
}

@Embeddable
class Table1Id implements Serializable {

@Column(name='key_field_1')
String key1

@Column(name='key_field_2')
String key2
}

@Entity
@Table(name='table2')
class Table2 {

@Id
@Column(name='key_id')
String keyId

@Column(name='field1')
String field1

@Column(name='field2')
String field2

@Column(name='field3')
String field3
}

我的 Spock 测试如下:

def "Try out the JOIN select with Criteria API"() {
given:

CriteriaBuilder cb = entityManager.getCriteriaBuilder()

CriteriaQuery<Object[]> cQuery = cb.createQuery(Object[].class)
Root<Table1> t1 = cQuery.from(Table1.class)
Path<Table2> t2 = t1.get('table2')
Join<Table1, Table2> lanyonLeftJoin = t1.join('table2', JoinType.INNER)

Predicate where = cb.equal(t1.get('itemSource'), 'ABC')

cQuery.multiselect(t1, t2)
cQuery.where(where)

when:
List<Object[]> result = entityManager.createQuery(cQuery).getResultList()

then:
result.each{ aRow ->
println "${aRow[0]}, ${aRow[1]}"
}
}

此配置成功在 Table1 和 Table2 之间生成 INNER JOIN,请注意,即使是“where”子句中的常量也能正确解释。

但是,由于某些奇怪的原因,Table2 会重新查询第一个查询中返回的每一行。

我看到的输出是:

Hibernate: 
select
table10_.key_field_1 as key_field_11_3_0_,
table10_.key_field_2 as key_field_22_3_0_,
table21_.key_id as key_id1_5_1_,
table10_.item_source as item_source3_3_0_,
table10_.sequence_num as sequence_num4_3_0_,
table10_.source_type as source_type5_3_0_,
table21_.field2 as field23_5_1_,
table21_.field3 as field34_5_1_,
table21_.field1 as field15_5_1_
from
table1 table10_
inner join
table2 table21_
on table10_.key_field_2=table21_.key_id
where
table10_.item_source=?
Hibernate:
select
table20_.key_id as key_id1_5_0_,
table20_.field2 as field23_5_0_,
table20_.field3 as field34_5_0_,
table20_.field1 as field15_5_0_
from
table2 table20_
where
table20_.key_id=?
Hibernate:
select
table20_.key_id as key_id1_5_0_,
table20_.field2 as field23_5_0_,
table20_.field3 as field34_5_0_,
table20_.field1 as field15_5_0_
from
table2 table20_
where
table20_.key_id=?

// 500+ more of these

正如我们所看到的,第一个查询成功返回了两个表中的所有行,它实际上正是我正在寻找的查询。然而,正在执行所有这些不必要的额外查询。

JPA 会这样做有什么原因吗?有没有办法阻止它?

我觉得我在这里遗漏了一些非常明显的东西。

预先感谢您的帮助


更新1

如果我更换

cQuery.multiselect(t1, t2)

对于

cQuery.multiselect(t1.get('id').get('key1'), t1.get('id').get('key2'), 
t1.get('fieldX'), t1.get('fieldY'), t1.get('fieldZ'),
t2.get('fieldA'), t2.get('fieldB'), t2.get('fieldC') ...)

它生成完全相同的内连接查询,并且不会再次重新查询 Table2。

换句话说,看起来(至少对于这种情况)我需要显式列出两个表中的所有字段。这不是一个很好的解决方法,因为对于具有大量字段的表来说,它很快就会变得非常难看。我想知道是否有一种方法可以检索所有 @Column 注释的字段/getter,而无需资源到一堆反射内容?

最佳答案

我想我已经做到了!

  1. @JoinFormula:

    Table2 中的主键是 INT,Table1 中用作 FK 的字段是 String(我完全错过了!废话!)。因此,解决方案是应用 @JoinFormula 而不是 @JoinColumn,其形式为:

    @OneToOne(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
    @JoinColumnsOrFormulas([
    @JoinColumnOrFormula(formula=@JoinFormula(value='CAST(key_field_2 AS INT)'))
    ])
    @NotFound(action=NotFoundAction.IGNORE)
    Table2 table2

    这会奇怪地返回一个 List List 的每个项目都包含一个包含 2 个元素的数组:一个 Table1 实例和一个 Table2 实例。

  2. 加入获取:

    根据您的建议,我在查询中添加了“join fetch”,所以它看起来像:

    select t1, t2 from Table1 t1 **join fetch** t1.table2 t2 where t1.itemSource = 'ABC'

    这会导致 Hibernate 正确返回 List

单独使用 @JoinFormula 或 @JoinFormula +“join fetch”hibernate 停止生成 n+1 查询。

调试 Hibernate 代码我发现它在第一次使用联接查询查询数据库时可以正确检索并在 session 中存储两个实体,但是 PK 和 FK 数据类型之间的差异导致 Hibernate 再次重新查询数据库,在第一个查询中检索的每行一次。

关于java - Spring JPA + Hibernate OneToOne 关系进行 n+1 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41730665/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com