Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/316.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 春季JPA+;Hibernate-OneToOne关系使n+;1查询_Java_Hibernate_Jpa_Groovy_Spring Boot - Fatal编程技术网

Java 春季JPA+;Hibernate-OneToOne关系使n+;1查询

Java 春季JPA+;Hibernate-OneToOne关系使n+;1查询,java,hibernate,jpa,groovy,spring-boot,Java,Hibernate,Jpa,Groovy,Spring Boot,我正在使用SpringBoot、Groovy和JPA/Hibernate迁移一个旧的应用程序。唯一的限制是不能更改数据库模型,我发现自己遇到了一个奇怪的情况,即OneOnOne关系: 请查看以下模型设置: @Entity @Table(name='table1') class Table1 { @Id Table1Id id @Column(name='sequence_num') Integer seqNum @Column(name='item_source')

我正在使用SpringBoot、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]}"
    }
}
正如我们所看到的,第一个查询成功地返回了两个表中的所有行,实际上这正是我要查找的查询。然而,所有这些不必要的额外查询都会被执行

JPA为什么会做这样的事情,有什么原因吗?有什么方法可以防止吗

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

提前谢谢你的帮助


更新1

如果我替换

cQuery.multiselect(t1, t2)
为了

它生成完全相同的内部联接查询,并且不再重新查询表2

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

我想我已经找到了

  • @联合公式:

    表2中的主键是INT,而表1中用作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
    
    这会奇怪地返回一个列表,列表中的每个项目都包含一个由2个元素组成的数组:一个Table1实例和一个Table2实例

  • 加入获取:

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

    select t1, t2 from Table1 t1 **join fetch** t1.table2 t2 where t1.itemSource = 'ABC'
    
    这会使Hibernate正确返回列表

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

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

    我想我找到了

  • @联合公式:

    表2中的主键是INT,而表1中用作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
    
    这会奇怪地返回一个列表,列表中的每个项目都包含一个由2个元素组成的数组:一个Table1实例和一个Table2实例

  • 加入获取:

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

    select t1, t2 from Table1 t1 **join fetch** t1.table2 t2 where t1.itemSource = 'ABC'
    
    这会使Hibernate正确返回列表

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


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

    Hi!“n+1查询问题”是ORM世界中一个广为人知的问题。你可以通过谷歌搜索得到无数的解释。至于修复它,我想您可以取消注释
    @Fetch(FetchMode.JOIN)
    。你有什么理由把它评论掉吗?我把它评论掉了,因为它似乎对这个问题没有影响。无论是否使用@Fetch,它仍然会进行所有这些额外的查询。我正在检查JPA/Hibernate代码中的代码,出于某种我还不太了解的原因,JPA似乎认为左侧连接中的实体尚未检索,并导致触发“secondPass”代码。。。。即使这样的实体已经作为“Table2”实例出现在结果中,所以我在一个旧项目中使用hibernate“join fetch”在HQL查询中解决了这个问题
    从个人p加入获取p.children
    。这将只执行一个查询。我原以为注释会提供类似的功能,但可能因为要通过JPA而没有得到使用?嗨!“n+1查询问题”是ORM世界中一个广为人知的问题。你可以通过谷歌搜索得到无数的解释。至于修复它,我想您可以取消注释
    @Fetch(FetchMode.JOIN)
    。你有什么理由把它评论掉吗?我把它评论掉了,因为它似乎对这个问题没有影响。无论是否使用@Fetch,它仍然会进行所有这些额外的查询。我正在检查JPA/Hibernate代码中的代码,出于某种我还不太了解的原因,JPA似乎认为左侧连接中的实体尚未检索,并导致触发“secondPass”代码。。。。即使这样的实体已经作为“Table2”实例出现在结果中,所以我在一个旧项目中使用hibernate“join fetch”在HQL查询中解决了这个问题
    从个人p加入获取p.children
    。这将只执行一个查询。我原以为注释会提供类似的功能,但可能因为要通过JPA而无法使用?