休眠JPA2-n + 1选择问题仅影响OneToOne关系的一侧

问题描述

| 在通过以下方法添加字节码检测之前,我不会在任何一个类上都进行延迟加载:
  <plugin>
    <artifactId>maven-antrun-plugin</artifactId>
    <executions>
      <execution>
        <phase>process-classes</phase>
        <goals>
          <goal>run</goal>
        </goals>
      </execution>
    </executions>
    <configuration>
      <tasks>
        <taskdef name=\"instrument\" classname=\"org.hibernate.tool.instrument.javassist.InstrumentTask\">
          <classpath>
            <path refid=\"maven.runtime.classpath\" />
            <path refid=\"maven.plugin.classpath\" />
          </classpath>
        </taskdef>
        <instrument verbose=\"false\">
          <fileset dir=\"${project.build.outputDirectory}\">
            <include name=\"**/db/**/*.class\" />
          </fileset>
        </instrument>
      </tasks>
    </configuration>
  </plugin>
这是我大大简化的两个实体类: \“来自Ininvgrmtr \”的表可以根据需要运行(无n + 1问题):
@Entity
@Table(name = \"ININVGRMTR\",catalog = \"CO05IN\",schema = \"\")
@XmlRootElement
public class Ininvgrmtr implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Size(min = 1,max = 8)
    @Column(name = \"IGMGRUP\",nullable = false,length = 8)
    private String igmgrup;

    //other attributes

    @JoinColumn(name = \"IGMGRUP\",referencedColumnName = \"IGGRUP\",insertable = false,updatable = false)
    @OnetoOne(optional = false,fetch=FetchType.LAZY)
    private Ininvgrp ininvgrp;
}
此表“来自Ininvgrp”不:
@Entity
@Table(name = \"ININVGRP\",schema = \"\")
@XmlRootElement
public class Ininvgrp implements Serializable {
    private static final long serialVersionUID = 1L;
    @Id
    @Basic(optional = false)
    @NotNull
    @Size(min = 1,max = 8)
    @Column(name = \"IGGRUP\",length = 8)
    private String iggrup;

    //other attributes

    @OnetoOne(cascade = CascadeType.ALL,mappedBy = \"ininvgrp\",fetch=FetchType.LAZY)
    private Ininvgrmtr ininvgrmtr;

    //getters setters
} 
为了说明问题:
entityManagerFactory.createEntityManager().createquery(\"from Ininvgrmtr\").getResultList();
将以下内容打印到日志中(很好):
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_,ininvgrmtr0_.IGMTRACE as IGMTRACE96_ from CO05IN.ININVGRMTR ininvgrmtr0_
而,
entityManagerFactory.createEntityManager().createquery(\"from Ininvgrp\").getResultList();
Prints打印以下内容
INFO: Hibernate: select ininvgrp0_.IGGRUP as IGGRUP97_,ininvgrp0_.Added as Added97_,ininvgrp0_.IGABCF as IGABCF97_,ininvgrp0_.IGADCN as IGADCN97_,ininvgrp0_.IGADDT as IGADDT97_,ininvgrp0_.IGADUS as IGADUS97_,ininvgrp0_.IGCAT as IGCAT97_,ininvgrp0_.IGDESC as IGDESC97_,ininvgrp0_.IGMDCN as IGMDCN97_,ininvgrp0_.IGMDDT as IGMDDT97_,ininvgrp0_.IGMDUS as IGMDUS97_,ininvgrp0_.IGRETH as IGRETH97_,ininvgrp0_.IGSTA as IGSTA97_,ininvgrp0_.IGTYPE as IGTYPE97_,ininvgrp0_.IGUBAS as IGUBAS97_,ininvgrp0_.IGUSEL as IGUSEL97_,ininvgrp0_.IGUWGT as IGUWGT97_,ininvgrp0_.Modified as Modified97_ from CO05IN.ININVGRP ininvgrp0_
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_,ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
INFO: Hibernate: select ininvgrmtr0_.IGMGRUP as IGMGRUP96_0_,ininvgrmtr0_.IGMTRACE as IGMTRACE96_0_ from CO05IN.ININVGRMTR ininvgrmtr0_ where ininvgrmtr0_.IGMGRUP=?
...
是什么原因造成的?     

解决方法

Ininvgrmtr
上的属性标记为不可为空且不是可选的,因此hibernate知道必须有一个具有给定id的实体。然后,Hibernate可以创建一个动态代理并将其设置为属性值。然后,仅当您访问其属性时,该代理才会被初始化。 另一方面,从
Ininvgrp
开始,默认情况下将该属性标记为可选。在这种情况下,Hibernate无法使用动态代理,因为如果没有匹配的实体,它必须返回null。 使用字节码编织,可以修改对字段本身的每次访问以查询数据库。如果可以在构建过程中使用字节码检测,那将是首选的解决方案。 另一个解决方法是将属性声明为
OneToMany
关系,并将其从空/一个元素列表转换为null或在getter和setter中的第一个元素,如下所示:
@OneToMany(cascade = CascadeType.ALL,mappedBy = \"ininvgrp\",fetch=FetchType.LAZY)
private List<Ininvgrmtr> ininvgrmtr;

public void setIninvgrmtr(Ininvgrmtr ininvgrmtr) {
    if (this.ininvgrmtr == null || this.ininvgrmtr.isEmpty()) {
        this.ininvgrmtr = Collections.singletonList(ininvgrmtr);
    } else {
        this.ininvgrmtr.set(0,ininvgrmtr);
    }
}

public Ininvgrmtr getIninvgrmtr() {
    return ininvgrmtr == null || ininvgrmtr.isEmpty() ? null : ininvgrmtr.get(0);
}
编辑:可以在此博客文章中找到有关该问题的更详细描述。